Merge "Remove framework faceDetect requirement"
diff --git a/Android.bp b/Android.bp
index 3d25bc1..c0a2abb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -102,7 +102,7 @@
         ":android.hardware.keymaster-V4-java-source",
         ":android.hardware.security.keymint-V3-java-source",
         ":android.hardware.security.secureclock-V1-java-source",
-        ":android.hardware.tv.tuner-V1-java-source",
+        ":android.hardware.tv.tuner-V2-java-source",
         ":android.security.apc-java-source",
         ":android.security.authorization-java-source",
         ":android.security.legacykeystore-java-source",
@@ -205,7 +205,7 @@
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.contexthub-V1.1-java",
         "android.hardware.contexthub-V1.2-java",
-        "android.hardware.contexthub-V1-java",
+        "android.hardware.contexthub-V2-java",
         "android.hardware.gnss-V1.0-java",
         "android.hardware.gnss-V2.1-java",
         "android.hardware.health-V1.0-java-constants",
diff --git a/core/api/current.txt b/core/api/current.txt
index 17b0d9e..f26f8399 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1261,6 +1261,7 @@
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
+    field public static final int requiredDisplayCategory;
     field public static final int requiredFeature = 16844116; // 0x1010554
     field public static final int requiredForAllUsers = 16843728; // 0x10103d0
     field public static final int requiredNotFeature = 16844117; // 0x1010555
@@ -1508,7 +1509,6 @@
     field public static final int targetCellWidth = 16844340; // 0x1010634
     field public static final int targetClass = 16842799; // 0x101002f
     field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
-    field public static final int targetDisplayCategory;
     field public static final int targetId = 16843740; // 0x10103dc
     field public static final int targetName = 16843853; // 0x101044d
     field public static final int targetPackage = 16842785; // 0x1010021
@@ -11201,10 +11201,10 @@
     field public String parentActivityName;
     field public String permission;
     field public int persistableMode;
+    field @Nullable public String requiredDisplayCategory;
     field public int screenOrientation;
     field public int softInputMode;
     field public String targetActivity;
-    field @Nullable public String targetDisplayCategory;
     field public String taskAffinity;
     field public int theme;
     field public int uiOptions;
@@ -23953,11 +23953,17 @@
   }
 
   public static final class RouteListingPreference.Item implements android.os.Parcelable {
-    ctor public RouteListingPreference.Item(@NonNull String);
+    ctor public RouteListingPreference.Item(@NonNull String, int, int);
     method public int describeContents();
+    method public int getDisableReason();
+    method public int getFlags();
     method @NonNull public String getRouteId();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference.Item> CREATOR;
+    field public static final int DISABLE_REASON_NONE = 0; // 0x0
+    field public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1; // 0x1
+    field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
+    field public static final int FLAG_SUGGESTED_ROUTE = 2; // 0x2
   }
 
   public final class RoutingSessionInfo implements android.os.Parcelable {
@@ -32702,7 +32708,6 @@
     method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.QUERY_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
     method public android.os.Bundle getUserRestrictions();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}) public java.util.Set<android.os.UserHandle> getVisibleUsers();
     method public boolean hasUserRestriction(String);
     method public boolean isDemoUser();
     method public static boolean isHeadlessSystemUserMode();
@@ -32716,7 +32721,6 @@
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public boolean isUserUnlocked();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserUnlocked(android.os.UserHandle);
-    method public boolean isUserVisible();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.MODIFY_QUIET_MODE"}, conditional=true) public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
     method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
     method @Deprecated public boolean setRestrictionsChallenge(String);
@@ -45672,6 +45676,19 @@
     method public int previousStartBoundary(@IntRange(from=0) int);
   }
 
+  public class Highlights {
+    method @NonNull public android.graphics.Paint getPaint(int);
+    method @NonNull public int[] getRanges(int);
+    method public int getSize();
+  }
+
+  public static final class Highlights.Builder {
+    ctor public Highlights.Builder();
+    method @NonNull public android.text.Highlights.Builder addRange(@NonNull android.graphics.Paint, int, int);
+    method @NonNull public android.text.Highlights.Builder addRanges(@NonNull android.graphics.Paint, @NonNull int...);
+    method @NonNull public android.text.Highlights build();
+  }
+
   public class Html {
     method public static String escapeHtml(CharSequence);
     method @Deprecated public static android.text.Spanned fromHtml(String);
@@ -45763,6 +45780,9 @@
     ctor protected Layout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float);
     method public void draw(android.graphics.Canvas);
     method public void draw(android.graphics.Canvas, android.graphics.Path, android.graphics.Paint, int);
+    method public void draw(@NonNull android.graphics.Canvas, @Nullable java.util.List<android.graphics.Path>, @Nullable java.util.List<android.graphics.Paint>, @Nullable android.graphics.Path, @Nullable android.graphics.Paint, int);
+    method public void drawBackground(@NonNull android.graphics.Canvas);
+    method public void drawText(@NonNull android.graphics.Canvas);
     method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
     method public final android.text.Layout.Alignment getAlignment();
     method public abstract int getBottomPadding();
@@ -58576,6 +58596,7 @@
     method public boolean getFreezesText();
     method public int getGravity();
     method @ColorInt public int getHighlightColor();
+    method @Nullable public android.text.Highlights getHighlights();
     method public CharSequence getHint();
     method public final android.content.res.ColorStateList getHintTextColors();
     method public int getHyphenationFrequency();
@@ -58705,6 +58726,7 @@
     method public void setGravity(int);
     method public void setHeight(int);
     method public void setHighlightColor(@ColorInt int);
+    method public void setHighlights(@Nullable android.text.Highlights);
     method public final void setHint(CharSequence);
     method public final void setHint(@StringRes int);
     method public final void setHintTextColor(@ColorInt int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3228ce6..286a800 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -393,7 +393,6 @@
     method public static void traceBegin(long, @NonNull String);
     method public static void traceCounter(long, @NonNull String, int);
     method public static void traceEnd(long);
-    field public static final long TRACE_TAG_AIDL = 16777216L; // 0x1000000L
     field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c3e9887..66423c8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2998,13 +2998,13 @@
 
   public static final class VirtualDeviceParams.Builder {
     ctor public VirtualDeviceParams.Builder();
-    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addDevicePolicy(int, int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addVirtualSensorConfig(@NonNull android.companion.virtual.sensor.VirtualSensorConfig);
     method @NonNull public android.companion.virtual.VirtualDeviceParams build();
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
+    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
     method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
@@ -7521,6 +7521,7 @@
     field public static final int VIDEO_STREAM_TYPE_VC1 = 7; // 0x7
     field public static final int VIDEO_STREAM_TYPE_VP8 = 8; // 0x8
     field public static final int VIDEO_STREAM_TYPE_VP9 = 9; // 0x9
+    field public static final int VIDEO_STREAM_TYPE_VVC = 13; // 0xd
   }
 
   public static class AvSettings.Builder {
@@ -7713,6 +7714,7 @@
     field public static final int INDEX_TYPE_SC = 1; // 0x1
     field public static final int INDEX_TYPE_SC_AVC = 3; // 0x3
     field public static final int INDEX_TYPE_SC_HEVC = 2; // 0x2
+    field public static final int INDEX_TYPE_SC_VVC = 4; // 0x4
     field public static final int MPT_INDEX_AUDIO = 262144; // 0x40000
     field public static final int MPT_INDEX_MPT = 65536; // 0x10000
     field public static final int MPT_INDEX_TIMESTAMP_TARGET_AUDIO = 1048576; // 0x100000
@@ -7735,6 +7737,13 @@
     field public static final int SC_INDEX_SEQUENCE = 8; // 0x8
     field public static final int SC_INDEX_SI_SLICE = 128; // 0x80
     field public static final int SC_INDEX_SP_SLICE = 256; // 0x100
+    field public static final int SC_VVC_INDEX_AUD = 64; // 0x40
+    field public static final int SC_VVC_INDEX_SLICE_CRA = 4; // 0x4
+    field public static final int SC_VVC_INDEX_SLICE_GDR = 8; // 0x8
+    field public static final int SC_VVC_INDEX_SLICE_IDR_N_LP = 2; // 0x2
+    field public static final int SC_VVC_INDEX_SLICE_IDR_W_RADL = 1; // 0x1
+    field public static final int SC_VVC_INDEX_SPS = 32; // 0x20
+    field public static final int SC_VVC_INDEX_VPS = 16; // 0x10
     field public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG = 4096; // 0x1000
     field public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED = 8; // 0x8
     field public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED = 4; // 0x4
@@ -10012,6 +10021,10 @@
     field public static final int STATUS_WAITING_REBOOT = 5; // 0x5
   }
 
+  public final class Trace {
+    field public static final long TRACE_TAG_AIDL = 16777216L; // 0x1000000L
+  }
+
   public class UpdateEngine {
     ctor public UpdateEngine();
     method @NonNull @WorkerThread public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
@@ -10111,6 +10124,7 @@
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
     method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
+    method @NonNull @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public java.util.Set<android.os.UserHandle> getVisibleUsers();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
@@ -10128,6 +10142,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isUserOfType(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public boolean isUserVisible();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
@@ -12275,6 +12290,7 @@
     method @Nullable public android.media.AudioTimestamp getTimestamp();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordAudioStream> CREATOR;
+    field public static final String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES = "android.service.voice.key.AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES";
   }
 
   public static final class HotwordAudioStream.Builder {
@@ -15900,6 +15916,7 @@
   public class ImsSmsImplBase {
     ctor public ImsSmsImplBase();
     method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
+    method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int, @NonNull byte[]);
     method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
     method public String getSmsFormat();
     method public void onReady();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 984e822..002032a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1316,6 +1316,14 @@
 
 }
 
+package android.hardware.location {
+
+  public final class ContextHubManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public long[] getPreloadedNanoAppIds(@NonNull android.hardware.location.ContextHubInfo);
+  }
+
+}
+
 package android.hardware.soundtrigger {
 
   public class KeyphraseEnrollmentInfo {
@@ -3274,6 +3282,7 @@
 
   public class Spinner extends android.widget.AbsSpinner implements android.content.DialogInterface.OnClickListener {
     method public boolean isPopupShowing();
+    method public void onClick(int);
   }
 
   @android.widget.RemoteViews.RemoteView public class TextClock extends android.widget.TextView {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index af0fa39..a4a12d7 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -417,6 +417,16 @@
     ],
 }
 
+// This file group is used by service fuzzer
+filegroup {
+    name: "framework-core-sources-for-fuzzers",
+    srcs: [
+        "android/os/IInterface.java",
+        "android/os/Binder.java",
+        "android/os/IBinder.java",
+    ],
+}
+
 aidl_interface {
     name: "android.os.statsbootstrap_aidl",
     unstable: true,
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 96ced41..897cd1f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6683,34 +6683,6 @@
         StrictMode.initThreadDefaults(data.appInfo);
         StrictMode.initVmDefaults(data.appInfo);
 
-        if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
-            // XXX should have option to change the port.
-            Debug.changeDebugPort(8100);
-            if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
-                      + " is waiting for the debugger on port 8100...");
-
-                IActivityManager mgr = ActivityManager.getService();
-                try {
-                    mgr.showWaitingForDebugger(mAppThread, true);
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-
-                Debug.waitForDebugger();
-
-                try {
-                    mgr.showWaitingForDebugger(mAppThread, false);
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-
-            } else {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
-                      + " can be debugged on port 8100...");
-            }
-        }
-
         // Allow binder tracing, and application-generated systrace messages if we're profileable.
         boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable();
@@ -6818,6 +6790,34 @@
             throw ex.rethrowFromSystemServer();
         }
 
+        // Wait for debugger after we have notified the system to finish attach application
+        if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
+            // XXX should have option to change the port.
+            Debug.changeDebugPort(8100);
+            if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
+                Slog.w(TAG, "Application " + data.info.getPackageName()
+                        + " is waiting for the debugger on port 8100...");
+
+                try {
+                    mgr.showWaitingForDebugger(mAppThread, true);
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+
+                Debug.waitForDebugger();
+
+                try {
+                    mgr.showWaitingForDebugger(mAppThread, false);
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+
+            } else {
+                Slog.w(TAG, "Application " + data.info.getPackageName()
+                        + " can be debugged on port 8100...");
+            }
+        }
+
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 63fdc2e..332aadd 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -58,6 +58,7 @@
 import android.healthconnect.HealthConnectManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.permission.PermissionCheckerManager;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -880,11 +881,12 @@
         int checkPermission(@NonNull Context context, @NonNull String name, int callerUid,
                 int callerPid, String packageName, boolean allowWhileInUse) {
             // Simple case, check if it's already granted.
-            if (PermissionChecker.checkPermissionForPreflight(context, name,
-                    callerPid, callerUid, packageName) == PERMISSION_GRANTED) {
+            @PackageManager.PermissionResult int result;
+            if ((result = PermissionChecker.checkPermissionForPreflight(context, name,
+                    callerPid, callerUid, packageName)) == PERMISSION_GRANTED) {
                 return PERMISSION_GRANTED;
             }
-            if (allowWhileInUse) {
+            if (allowWhileInUse && result == PermissionCheckerManager.PERMISSION_SOFT_DENIED) {
                 // Check its appops
                 final int opCode = AppOpsManager.permissionToOpCode(name);
                 final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index f92194d..2f51b17 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -213,7 +213,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public @GameMode int[] getAvailableGameModes(@NonNull String packageName) {
         try {
-            return mService.getAvailableGameModes(packageName);
+            return mService.getAvailableGameModes(packageName, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index aea097d..3d6ab6f 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -29,7 +29,7 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
     void setGameMode(String packageName, int gameMode, int userId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
-    int[] getAvailableGameModes(String packageName);
+    int[] getAvailableGameModes(String packageName, int userId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
     boolean isAngleEnabled(String packageName, int userId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)")
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5d87012..762ac23 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1617,6 +1617,7 @@
                 case Context.INCREMENTAL_SERVICE:
                 case Context.ETHERNET_SERVICE:
                 case Context.CONTEXTHUB_SERVICE:
+                case Context.VIRTUALIZATION_SERVICE:
                     return null;
             }
             Slog.wtf(TAG, "Manager wrapper not available: " + name);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 01b42bf..568185a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -416,7 +416,7 @@
          * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
          * @param displayCategories The categories of the virtual display, indicating the type of
          * activities allowed to run on the display. Activities can declare their type using
-         * {@link android.content.pm.ActivityInfo#targetDisplayCategory}.
+         * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
          * @param surface The surface to which the content of the virtual display should
          * be rendered, or null if there is none initially. The surface can also be set later using
          * {@link VirtualDisplay#setSurface(Surface)}.
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index bad26c6..1cbe910 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -313,7 +313,7 @@
      * Returns the policy specified for this policy type, or {@link #DEVICE_POLICY_DEFAULT} if no
      * policy for this type has been explicitly specified.
      *
-     * @see Builder#addDevicePolicy
+     * @see Builder#setDevicePolicy
      */
     public @DevicePolicy int getDevicePolicy(@PolicyType int policyType) {
         return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
@@ -615,16 +615,16 @@
         }
 
         /**
-         * Add a policy for this virtual device.
+         * Specifies a policy for this virtual device.
          *
-         * Policies define the system behavior that may be specific for this virtual device. A
+         * <p>Policies define the system behavior that may be specific for this virtual device. A
          * policy can be defined for each {@code PolicyType}, but they are all optional.
          *
          * @param policyType the type of policy, i.e. which behavior to specify a policy for.
          * @param devicePolicy the value of the policy, i.e. how to interpret the device behavior.
          */
         @NonNull
-        public Builder addDevicePolicy(@PolicyType int policyType, @DevicePolicy int devicePolicy) {
+        public Builder setDevicePolicy(@PolicyType int policyType, @DevicePolicy int devicePolicy) {
             mDevicePolicies.put(policyType, devicePolicy);
             return this;
         }
@@ -632,13 +632,13 @@
         /**
          * Adds a configuration for a sensor that should be created for this virtual device.
          *
-         * Device sensors must remain valid for the entire lifetime of the device, hence they are
+         * <p>Device sensors must remain valid for the entire lifetime of the device, hence they are
          * created together with the device itself, and removed when the device is removed.
          *
-         * Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_SENSORS}.
+         * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_SENSORS}.
          *
          * @see android.companion.virtual.sensor.VirtualSensor
-         * @see #addDevicePolicy
+         * @see #setDevicePolicy
          */
         @NonNull
         public Builder addVirtualSensorConfig(@NonNull VirtualSensorConfig virtualSensorConfig) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9d82274..65b3ca4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3889,7 +3889,7 @@
             "android.intent.action.USER_INITIALIZE";
 
     /**
-     * Sent when a user switch is happening, causing the process's user to be
+     * Sent after a user switch is complete, if the switch caused the process's user to be
      * brought to the foreground.  This is only sent to receivers registered
      * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
      * Context.registerReceiver}.  It is sent to the user that is going to the
@@ -3901,7 +3901,7 @@
             "android.intent.action.USER_FOREGROUND";
 
     /**
-     * Sent when a user switch is happening, causing the process's user to be
+     * Sent after a user switch is complete, if the switch caused the process's user to be
      * sent to the background.  This is only sent to receivers registered
      * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
      * Context.registerReceiver}.  It is sent to the user that is going to the
@@ -4042,6 +4042,11 @@
      * the current state of the user.
      * @hide
      */
+    /*
+     * This broadcast is sent after the user switch is complete. In case a task needs to be done
+     * while the switch is happening (i.e. while the screen is frozen to hide UI jank), please use
+     * ActivityManagerService.registerUserSwitchObserver method.
+     */
     @SystemApi
     public static final String ACTION_USER_SWITCHED =
             "android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index fda4119..dab57fd 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -221,21 +221,20 @@
     public String launchToken;
 
     /**
-     * Specifies the category of the target display the activity is expected to run on. Set from
-     * the {@link android.R.attr#targetDisplayCategory} attribute. Upon creation, a virtual display
-     * can specify which display categories it supports and one of the category must be present in
-     * the activity's manifest to allow this activity to run. The default value is {@code null},
-     * which indicates the activity does not belong to a restricted display category and thus can
-     * only run on a display that didn't specify any display categories. Each activity can only
-     * specify one category it targets to but a virtual display can support multiple restricted
-     * categories.
-     *
+     * Specifies the required display category of the activity. Set from the
+     * {@link android.R.attr#requiredDisplayCategory} attribute. Upon creation, a display can
+     * specify which display categories it supports and one of the category must be present
+     * in the {@code <activity>} element to allow this activity to run. The default value is
+     * {@code null}, which indicates the activity does not have a required display category and
+     * thus can only run on a display that didn't specify any display categories. Each activity
+     * can only specify one required category but a display can support multiple display categories.
+     * <p>
      * This field should be formatted as a Java-language-style free form string(for example,
      * com.google.automotive_entertainment), which may contain uppercase or lowercase letters ('A'
      * through 'Z'), numbers, and underscores ('_') but may only start with letters.
      */
     @Nullable
-    public String targetDisplayCategory;
+    public String requiredDisplayCategory;
 
     /**
      * Activity can not be resized and always occupies the fullscreen area with all windows fully
@@ -1330,7 +1329,7 @@
         mMaxAspectRatio = orig.mMaxAspectRatio;
         mMinAspectRatio = orig.mMinAspectRatio;
         supportsSizeChanges = orig.supportsSizeChanges;
-        targetDisplayCategory = orig.targetDisplayCategory;
+        requiredDisplayCategory = orig.requiredDisplayCategory;
     }
 
     /**
@@ -1669,8 +1668,8 @@
         if (mKnownActivityEmbeddingCerts != null) {
             pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
         }
-        if (targetDisplayCategory != null) {
-            pw.println(prefix + "targetDisplayCategory=" + targetDisplayCategory);
+        if (requiredDisplayCategory != null) {
+            pw.println(prefix + "requiredDisplayCategory=" + requiredDisplayCategory);
         }
         super.dumpBack(pw, prefix, dumpFlags);
     }
@@ -1718,7 +1717,7 @@
         dest.writeFloat(mMinAspectRatio);
         dest.writeBoolean(supportsSizeChanges);
         sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
-        dest.writeString8(targetDisplayCategory);
+        dest.writeString8(requiredDisplayCategory);
     }
 
     /**
@@ -1844,7 +1843,7 @@
         if (mKnownActivityEmbeddingCerts.isEmpty()) {
             mKnownActivityEmbeddingCerts = null;
         }
-        targetDisplayCategory = source.readString8();
+        requiredDisplayCategory = source.readString8();
     }
 
     /**
diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java
index c7fdb16..457225d 100644
--- a/core/java/android/content/res/FontScaleConverter.java
+++ b/core/java/android/content/res/FontScaleConverter.java
@@ -36,11 +36,6 @@
  * @hide
  */
 public class FontScaleConverter {
-    /**
-     * How close the given SP should be to a canonical SP in the array before they are considered
-     * the same for lookup purposes.
-     */
-    private static final float THRESHOLD_FOR_MATCHING_SP = 0.02f;
 
     @VisibleForTesting
     final float[] mFromSpValues;
@@ -78,10 +73,11 @@
     public float convertSpToDp(float sp) {
         final float spPositive = Math.abs(sp);
         // TODO(b/247861374): find a match at a higher index?
-        final int spRounded = Math.round(spPositive);
         final float sign = Math.signum(sp);
-        final int index = Arrays.binarySearch(mFromSpValues, spRounded);
-        if (index >= 0 && Math.abs(spRounded - spPositive) < THRESHOLD_FOR_MATCHING_SP) {
+        // We search for exact matches only, even if it's just a little off. The interpolation will
+        // handle any non-exact matches.
+        final int index = Arrays.binarySearch(mFromSpValues, spPositive);
+        if (index >= 0) {
             // exact match, return the matching dp
             return sign * mToDpValues[index];
         } else {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 441fd88..f7675e8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -588,18 +588,20 @@
      * @see #DISPLAY_CATEGORY_PRESENTATION
      */
     public Display[] getDisplays(String category) {
-        final int[] displayIds = mGlobal.getDisplayIds();
+        boolean includeDisabled = (category != null
+                && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
+        final int[] displayIds = mGlobal.getDisplayIds(includeDisabled);
         synchronized (mLock) {
             try {
-                if (category == null
-                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
-                    addAllDisplaysLocked(mTempDisplays, displayIds);
-                } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+                if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
+                } else if (category == null
+                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+                    addAllDisplaysLocked(mTempDisplays, displayIds);
                 }
                 return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
             } finally {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cc397d5..f038c66 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -212,6 +212,16 @@
      */
     @UnsupportedAppUsage
     public int[] getDisplayIds() {
+        return getDisplayIds(/* includeDisabled= */ false);
+    }
+
+    /**
+     * Gets all currently valid logical display ids.
+     *
+     * @param includeDisabled True if the returned list of displays includes disabled displays.
+     * @return An array containing all display ids.
+     */
+    public int[] getDisplayIds(boolean includeDisabled) {
         try {
             synchronized (mLock) {
                 if (USE_CACHE) {
@@ -220,7 +230,7 @@
                     }
                 }
 
-                int[] displayIds = mDm.getDisplayIds();
+                int[] displayIds = mDm.getDisplayIds(includeDisabled);
                 if (USE_CACHE) {
                     mDisplayIdCache = displayIds;
                 }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 6b5594b..28bb35f 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -37,7 +37,7 @@
 interface IDisplayManager {
     @UnsupportedAppUsage
     DisplayInfo getDisplayInfo(int displayId);
-    int[] getDisplayIds();
+    int[] getDisplayIds(boolean includeDisabled);
 
     boolean isUidPresentOnDisplay(int uid, int displayId);
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fb5ac5a..6f63dbf 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -956,7 +956,7 @@
     public void onPointerDown(long requestId, int sensorId, int x, int y,
             float minor, float major) {
         if (mService == null) {
-            Slog.w(TAG, "onFingerDown: no fingerprint service");
+            Slog.w(TAG, "onPointerDown: no fingerprint service");
             return;
         }
 
@@ -979,7 +979,7 @@
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
     public void onPointerUp(long requestId, int sensorId) {
         if (mService == null) {
-            Slog.w(TAG, "onFingerDown: no fingerprint service");
+            Slog.w(TAG, "onPointerUp: no fingerprint service");
             return;
         }
 
@@ -993,6 +993,58 @@
     }
 
     /**
+     * TODO(b/218388821): The parameter list should be replaced with PointerContext.
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void onPointerDown(
+            long requestId,
+            int sensorId,
+            int pointerId,
+            float x,
+            float y,
+            float minor,
+            float major,
+            float orientation,
+            long time,
+            long gestureStart,
+            boolean isAod) {
+        if (mService == null) {
+            Slog.w(TAG, "onPointerDown: no fingerprint service");
+            return;
+        }
+
+        // TODO(b/218388821): Propagate all the parameters to FingerprintService.
+        Slog.e(TAG, "onPointerDown: not implemented!");
+    }
+
+    /**
+     * TODO(b/218388821): The parameter list should be replaced with PointerContext.
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void onPointerUp(
+            long requestId,
+            int sensorId,
+            int pointerId,
+            float x,
+            float y,
+            float minor,
+            float major,
+            float orientation,
+            long time,
+            long gestureStart,
+            boolean isAod) {
+        if (mService == null) {
+            Slog.w(TAG, "onPointerUp: no fingerprint service");
+            return;
+        }
+
+        // TODO(b/218388821): Propagate all the parameters to FingerprintService.
+        Slog.e(TAG, "onPointerUp: not implemented!");
+    }
+
+    /**
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index b54da6c..ac23af4 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -966,6 +967,34 @@
     }
 
     /**
+     * Queries for the list of preloaded nanoapp IDs on the system.
+     *
+     * @param hubInfo The Context Hub to query a list of nanoapp IDs from.
+     *
+     * @return The list of 64-bit IDs of the preloaded nanoapps.
+     *
+     * @throws NullPointerException if hubInfo is null
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+    @NonNull public long[] getPreloadedNanoAppIds(@NonNull ContextHubInfo hubInfo) {
+        Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
+
+        long[] nanoappIds = null;
+        try {
+            nanoappIds = mService.getPreloadedNanoAppIds(hubInfo);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        if (nanoappIds == null) {
+            nanoappIds = new long[0];
+        }
+        return nanoappIds;
+    }
+
+    /**
      * Unregister a callback for receive messages from the context hub.
      *
      * @see Callback
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index ced75c4..490267f 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -109,4 +109,8 @@
     // Queries for a list of nanoapps
     @EnforcePermission("ACCESS_CONTEXT_HUB")
     void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback);
+
+    // Queries for a list of preloaded nanoapps
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
+    long[] getPreloadedNanoAppIds(in ContextHubInfo hubInfo);
 }
diff --git a/core/java/android/nfc/AvailableNfcAntenna.java b/core/java/android/nfc/AvailableNfcAntenna.java
index 946ba67..6e6512a 100644
--- a/core/java/android/nfc/AvailableNfcAntenna.java
+++ b/core/java/android/nfc/AvailableNfcAntenna.java
@@ -27,13 +27,15 @@
  */
 public final class AvailableNfcAntenna implements Parcelable {
     /**
-     * Location on the antenna on the Y axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen.
+     * Location of the antenna on the Y axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
      */
     private final int mLocationX;
     /**
-     * Location on the antenna on the Y axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen.
+     * Location of the antenna on the Y axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
      */
     private final int mLocationY;
 
@@ -43,16 +45,18 @@
     }
 
     /**
-     * Location on the antenna on the X axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen.
+     * Location of the antenna on the X axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
      */
     public int getLocationX() {
         return mLocationX;
     }
 
     /**
-     * Location on the antenna on the Y axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen.
+     * Location of the antenna on the Y axis in millimeters.
+     * 0 is the bottom-left when the user is facing the screen
+     * and the device orientation is Portrait.
      */
     public int getLocationY() {
         return mLocationY;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index fb197f5..cdde18a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -100,7 +100,7 @@
     /** @hide */
     public static final long TRACE_TAG_VIBRATOR = 1L << 23;
     /** @hide */
-    @SystemApi(client = MODULE_LIBRARIES)
+    @SystemApi
     public static final long TRACE_TAG_AIDL = 1L << 24;
     /** @hide */
     public static final long TRACE_TAG_NNAPI = 1L << 25;
@@ -241,9 +241,16 @@
     /**
      * Writes a trace message to indicate that a given section of code has
      * begun. Must be followed by a call to {@link #asyncTraceEnd} using the same
-     * tag. Unlike {@link #traceBegin(long, String)} and {@link #traceEnd(long)},
-     * asynchronous events do not need to be nested. The name and cookie used to
-     * begin an event must be used to end it.
+     * tag, name and cookie.
+     *
+     * If two events with the same methodName overlap in time then they *must* have
+     * different cookie values. If they do not, the trace can become corrupted
+     * in unpredictable ways.
+     *
+     * Unlike {@link #traceBegin(long, String)} and {@link #traceEnd(long)},
+     * asynchronous events cannot be not nested. Consider using
+     * {@link #asyncTraceForTrackBegin(long, String, String, int)}
+     * if nested asynchronous events are needed.
      *
      * @param traceTag The trace tag.
      * @param methodName The method name to appear in the trace.
@@ -264,6 +271,9 @@
      * Must be called exactly once for each call to {@link #asyncTraceBegin(long, String, int)}
      * using the same tag, name and cookie.
      *
+     * See the documentation for {@link #asyncTraceBegin(long, String, int)}.
+     * for inteded usage of this method.
+     *
      * @param traceTag The trace tag.
      * @param methodName The method name to appear in the trace.
      * @param cookie Unique identifier for distinguishing simultaneous events
@@ -283,14 +293,73 @@
      * Writes a trace message to indicate that a given section of code has
      * begun. Must be followed by a call to {@link #asyncTraceForTrackEnd} using the same
      * track name and cookie.
-     * This function operates exactly like {@link #asyncTraceBegin(long, String, int)},
-     * except with the inclusion of a track name argument for where this method should appear.
-     * The cookie must be unique on the trackName level, not the methodName level
+     *
+     * Events with the same trackName and cookie nest inside each other in the
+     * same way as calls to {@link #traceBegin(long, String)} and
+     * {@link #traceEnd(long)}.
+     *
+     * If two events with the same trackName overlap in time but do not nest
+     * correctly, then they *must* have different cookie values. If they do not,
+     * the trace can become corrupted in unpredictable ways.
+     *
+     * Good Example:
+     *
+     * public void parent() {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "Track", "parent", mId);
+     *   child()
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "Track", mId);
+     * }
+     *
+     * public void child() {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "Track", "child", mId);
+     *   // Some code here.
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "Track", mId);
+     * }
+     *
+     * This would be visualized as so:
+     *   [   Parent   ]
+     *     [ Child ]
+     *
+     * Bad Example:
+     *
+     * public static void processData(String dataToProcess) {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "processDataInParallel", "processData", 0);
+     *   // Some code here.
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "processDataInParallel", 0);
+     * }
+     *
+     * public static void processDataInParallel({@code List<String>} data) {
+     *   ExecutorService executor = Executors.newCachedThreadPool();
+     *   for (String s : data) {
+     *     pool.execute(() -> processData(s));
+     *   }
+     * }
+     *
+     * This is invalid because it's possible for processData to be run many times
+     * in parallel (i.e. processData events overlap) but the same cookie is
+     * provided each time.
+     *
+     * To fix this, specify a different id in each invocation of processData:
+     *
+     * public static void processData(String dataToProcess, int id) {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "processDataInParallel", "processData", id);
+     *   // Some code here.
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "processDataInParallel", id);
+     * }
+     *
+     * public static void processDataInParallel({@code List<String>} data) {
+     *   ExecutorService executor = Executors.newCachedThreadPool();
+     *   for (int i = 0; i < data.size(); ++i) {
+     *     pool.execute(() -> processData(data.get(i), i));
+     *   }
+     * }
      *
      * @param traceTag The trace tag.
      * @param trackName The track where the event should appear in the trace.
      * @param methodName The method name to appear in the trace.
-     * @param cookie Unique identifier for distinguishing simultaneous events
+     * @param cookie Unique identifier used for nesting events on a single
+     *               track. Events which overlap without nesting on the same
+     *               track must have different values for cookie.
      *
      * @hide
      */
@@ -307,9 +376,14 @@
      * {@link #asyncTraceForTrackBegin(long, String, String, int)}
      * using the same tag, track name, and cookie.
      *
+     * See the documentation for {@link #asyncTraceForTrackBegin(long, String, String, int)}.
+     * for inteded usage of this method.
+     *
      * @param traceTag The trace tag.
      * @param trackName The track where the event should appear in the trace.
-     * @param cookie Unique identifier for distinguishing simultaneous events
+     * @param cookie Unique identifier used for nesting events on a single
+     *               track. Events which overlap without nesting on the same
+     *               track must have different values for cookie.
      *
      * @hide
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f76e8cd..dd02e02 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2976,8 +2976,15 @@
      * </ol>
      *
      * @return whether the user is visible at the moment, as defined above.
+     *
+     * @hide
      */
+    @SystemApi
     @UserHandleAware
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.MANAGE_USERS"
+    })
     public boolean isUserVisible() {
         try {
             return mService.isUserVisible(mUserId);
@@ -2990,9 +2997,14 @@
      * Gets the visible users (as defined by {@link #isUserVisible()}.
      *
      * @return visible users at the moment.
+     *
+     * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.INTERACT_ACROSS_USERS})
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.MANAGE_USERS"
+    })
     public @NonNull Set<UserHandle> getVisibleUsers() {
         ArraySet<UserHandle> result = new ArraySet<>();
         try {
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
index 1c57700..6ae952c 100644
--- a/core/java/android/service/voice/HotwordAudioStream.java
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -47,6 +47,21 @@
 public final class HotwordAudioStream implements Parcelable {
 
     /**
+     * Key for int value to be read from {@link #getMetadata()}. The value is read by the system and
+     * is the length (in bytes) of the byte buffers created to copy bytes in the
+     * {@link #getAudioStreamParcelFileDescriptor()} written by the {@link HotwordDetectionService}.
+     * The buffer length should be chosen such that no additional latency is introduced. Typically,
+     * this should be <em>at least</em> the size of byte chunks written by the
+     * {@link HotwordDetectionService}.
+     *
+     * <p>If no value specified in the metadata for the buffer length, or if the value is less than
+     * 1, or if it is greater than 65,536, or if it is not an int, the default value of 2,560 will
+     * be used.</p>
+     */
+    public static final String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES =
+            "android.service.voice.key.AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES";
+
+    /**
      * The {@link AudioFormat} of the audio stream.
      */
     @NonNull
@@ -414,10 +429,10 @@
     }
 
     @DataClass.Generated(
-            time = 1669184301563L,
+            time = 1669916341034L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
-            inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static  android.media.AudioTimestamp defaultTimestamp()\nprivate static  android.os.PersistableBundle defaultMetadata()\npublic  android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
+            inputSignatures = "public static final  java.lang.String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static  android.media.AudioTimestamp defaultTimestamp()\nprivate static  android.os.PersistableBundle defaultMetadata()\npublic  android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/text/Highlights.java b/core/java/android/text/Highlights.java
new file mode 100644
index 0000000..356dfca
--- /dev/null
+++ b/core/java/android/text/Highlights.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.graphics.Paint;
+import android.util.Pair;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that represents of the highlight of the text.
+ */
+public class Highlights {
+    private final List<Pair<Paint, int[]>> mHighlights;
+
+    private Highlights(List<Pair<Paint, int[]>> highlights) {
+        mHighlights = highlights;
+    }
+
+    /**
+     * Returns a number of highlight.
+     *
+     * @return a number of highlight.
+     *
+     * @see Builder#addRange(Paint, int, int)
+     * @see Builder#addRanges(Paint, int...)
+     */
+    public @IntRange(from = 0) int getSize() {
+        return mHighlights.size();
+    }
+
+    /**
+     * Returns a paint used for the i-th highlight.
+     *
+     * @param index an index of the highlight. Must be between 0 and {@link #getSize()}
+     * @return a paint object
+     *
+     * @see Builder#addRange(Paint, int, int)
+     * @see Builder#addRanges(Paint, int...)
+     */
+    public @NonNull Paint getPaint(@IntRange(from = 0) int index) {
+        return mHighlights.get(index).first;
+    }
+
+    /**
+     * Returns ranges of the i-th highlight.
+     *
+     * Ranges are represented of flattened inclusive start and exclusive end integers array. The
+     * inclusive start offset of the {@code i}-th range is stored in {@code 2 * i}-th of the array.
+     * The exclusive end offset of the {@code i}-th range is stored in {@code 2* i + 1}-th of the
+     * array. For example, the two ranges: (1, 2) and (3, 4) are flattened into single int array
+     * [1, 2, 3, 4].
+     *
+     * @param index an index of the highlight. Must be between 0 and {@link #getSize()}
+     * @return a paint object
+     *
+     * @see Builder#addRange(Paint, int, int)
+     * @see Builder#addRanges(Paint, int...)
+     */
+    public @NonNull int[] getRanges(int index) {
+        return mHighlights.get(index).second;
+    }
+
+    /**
+     * A builder for the Highlights.
+     */
+    public static final class Builder {
+        private final List<Pair<Paint, int[]>> mHighlights = new ArrayList<>();
+
+        /**
+         * Add single range highlight.
+         *
+         * @param paint a paint object used for drawing highlight path.
+         * @param start an inclusive offset of the text.
+         * @param end an exclusive offset of the text.
+         * @return this builder instance.
+         */
+        public @NonNull Builder addRange(@NonNull Paint paint, @IntRange(from = 0) int start,
+                @IntRange(from = 0) int end) {
+            if (start > end) {
+                throw new IllegalArgumentException("start must not be larger than end: "
+                        + start + ", " + end);
+            }
+            Objects.requireNonNull(paint);
+
+            int[] range = new int[] {start, end};
+            mHighlights.add(new Pair<>(paint, range));
+            return this;
+        }
+
+        /**
+         * Add multiple ranges highlight.
+         *
+         * @param paint a paint object used for drawing highlight path.
+         * @param ranges a flatten ranges. The {@code 2 * i}-th element is an inclusive start offset
+         *              of the {@code i}-th character. The {@code 2 * i + 1}-th element is an
+         *              exclusive end offset of the {@code i}-th character.
+         * @return this builder instance.
+         */
+        public @NonNull Builder addRanges(@NonNull Paint paint, @NonNull int... ranges) {
+            if (ranges.length % 2 == 1) {
+                throw new IllegalArgumentException(
+                        "Flatten ranges must have even numbered elements");
+            }
+            for (int j = 0; j < ranges.length / 2; ++j) {
+                int start = ranges[j * 2];
+                int end = ranges[j * 2 + 1];
+                if (start > end) {
+                    throw new IllegalArgumentException(
+                            "Reverse range found in the flatten range: " + Arrays.toString(
+                                    ranges));
+                }
+            }
+            Objects.requireNonNull(paint);
+            mHighlights.add(new Pair<>(paint, ranges));
+            return this;
+        }
+
+        /**
+         * Build a new Highlights instance.
+         *
+         * @return a new Highlights instance.
+         */
+        public @NonNull Highlights build() {
+            return new Highlights(mHighlights);
+        }
+    }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 54ec07e..64dc16d 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -44,6 +44,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * A base class that manages text layout in visual elements on
@@ -347,9 +348,13 @@
 
     /**
      * Draw this Layout on the specified Canvas.
+     *
+     * This API draws background first, then draws text on top of it.
+     *
+     * @see #draw(Canvas, List, List, Path, Paint, int)
      */
     public void draw(Canvas c) {
-        draw(c, null, null, 0);
+        draw(c, (Path) null, (Paint) null, 0);
     }
 
     /**
@@ -357,23 +362,142 @@
      * between the background and the text.
      *
      * @param canvas the canvas
-     * @param highlight the path of the highlight or cursor; can be null
-     * @param highlightPaint the paint for the highlight
+     * @param selectionHighlight the path of the selection highlight or cursor; can be null
+     * @param selectionHighlightPaint the paint for the selection highlight
      * @param cursorOffsetVertical the amount to temporarily translate the
      *        canvas while rendering the highlight
+     *
+     * @see #draw(Canvas, List, List, Path, Paint, int)
      */
-    public void draw(Canvas canvas, Path highlight, Paint highlightPaint,
+    public void draw(
+            Canvas canvas, Path selectionHighlight,
+            Paint selectionHighlightPaint, int cursorOffsetVertical) {
+        draw(canvas, null, null, selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
+    }
+
+    /**
+     * Draw this layout on the specified canvas.
+     *
+     * This API draws background first, then draws highlight paths on top of it, then draws
+     * selection or cursor, then finally draws text on top of it.
+     *
+     * @see #drawBackground(Canvas)
+     * @see #drawText(Canvas)
+     *
+     * @param canvas the canvas
+     * @param highlightPaths the path of the highlights. The highlightPaths and highlightPaints must
+     *                      have the same length and aligned in the same order. For example, the
+     *                      paint of the n-th of the highlightPaths should be stored at the n-th of
+     *                      highlightPaints.
+     * @param highlightPaints the paints for the highlights. The highlightPaths and highlightPaints
+     *                        must have the same length and aligned in the same order. For example,
+     *                        the paint of the n-th of the highlightPaths should be stored at the
+     *                        n-th of highlightPaints.
+     * @param selectionPath the selection or cursor path
+     * @param selectionPaint the paint for the selection or cursor.
+     * @param cursorOffsetVertical the amount to temporarily translate the canvas while rendering
+     *                            the highlight
+     */
+    public void draw(@NonNull Canvas canvas,
+            @Nullable List<Path> highlightPaths,
+            @Nullable List<Paint> highlightPaints,
+            @Nullable Path selectionPath,
+            @Nullable Paint selectionPaint,
             int cursorOffsetVertical) {
         final long lineRange = getLineRangeForDraw(canvas);
         int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
         int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
         if (lastLine < 0) return;
 
-        drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,
-                firstLine, lastLine);
+        drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+                cursorOffsetVertical, firstLine, lastLine);
         drawText(canvas, firstLine, lastLine);
     }
 
+    /**
+     * Draw text part of this layout.
+     *
+     * Different from {@link #draw(Canvas, List, List, Path, Paint, int)} API, this API only draws
+     * text part, not drawing highlights, selections, or backgrounds.
+     *
+     * @see #draw(Canvas, List, List, Path, Paint, int)
+     * @see #drawBackground(Canvas)
+     *
+     * @param canvas the canvas
+     */
+    public void drawText(@NonNull Canvas canvas) {
+        final long lineRange = getLineRangeForDraw(canvas);
+        int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
+        int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
+        if (lastLine < 0) return;
+        drawText(canvas, firstLine, lastLine);
+    }
+
+    /**
+     * Draw background of this layout.
+     *
+     * Different from {@link #draw(Canvas, List, List, Path, Paint, int)} API, this API only draws
+     * background, not drawing text, highlights or selections. The background here is drawn by
+     * {@link LineBackgroundSpan} attached to the text.
+     *
+     * @see #draw(Canvas, List, List, Path, Paint, int)
+     * @see #drawText(Canvas)
+     *
+     * @param canvas the canvas
+     */
+    public void drawBackground(@NonNull Canvas canvas) {
+        final long lineRange = getLineRangeForDraw(canvas);
+        int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
+        int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
+        if (lastLine < 0) return;
+        drawBackground(canvas, firstLine, lastLine);
+    }
+
+    /**
+     * @hide public for Editor.java
+     */
+    public void drawWithoutText(
+            @NonNull Canvas canvas,
+            @Nullable List<Path> highlightPaths,
+            @Nullable List<Paint> highlightPaints,
+            @Nullable Path selectionPath,
+            @Nullable Paint selectionPaint,
+            int cursorOffsetVertical,
+            int firstLine,
+            int lastLine) {
+        drawBackground(canvas, firstLine, lastLine);
+        if (highlightPaths == null && highlightPaints == null) {
+            return;
+        }
+        if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
+        try {
+            if (highlightPaths != null) {
+                if (highlightPaints == null) {
+                    throw new IllegalArgumentException(
+                            "if highlight is specified, highlightPaint must be specified.");
+                }
+                if (highlightPaints.size() != highlightPaths.size()) {
+                    throw new IllegalArgumentException(
+                            "The highlight path size is different from the size of highlight"
+                                    + " paints");
+                }
+                for (int i = 0; i < highlightPaths.size(); ++i) {
+                    final Path highlight = highlightPaths.get(i);
+                    final Paint highlightPaint = highlightPaints.get(i);
+                    if (highlight != null) {
+                        canvas.drawPath(highlight, highlightPaint);
+                    }
+                }
+            }
+
+            if (selectionPath != null) {
+                canvas.drawPath(selectionPath, selectionPaint);
+            }
+        } finally {
+            if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical);
+        }
+    }
+
     private boolean isJustificationRequired(int lineNum) {
         if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false;
         final int lineEnd = getLineEnd(lineNum);
@@ -635,8 +759,9 @@
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void drawBackground(Canvas canvas, Path highlight, Paint highlightPaint,
-            int cursorOffsetVertical, int firstLine, int lastLine) {
+    public void drawBackground(
+            @NonNull Canvas canvas,
+            int firstLine, int lastLine) {
         // First, draw LineBackgroundSpans.
         // LineBackgroundSpans know nothing about the alignment, margins, or
         // direction of the layout or line.  XXX: Should they?
@@ -700,14 +825,6 @@
             }
             mLineBackgroundSpans.recycle();
         }
-
-        // There can be a highlight even without spans if we are drawing
-        // a non-spanned transformation of a spanned editing buffer.
-        if (highlight != null) {
-            if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
-            canvas.drawPath(highlight, highlightPaint);
-            if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical);
-        }
     }
 
     /**
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index c54d9b6..3e7c67e 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -25,6 +25,7 @@
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
@@ -193,6 +194,29 @@
     }
 
     /**
+     * Same as {@link #rotatePoint}, but for float coordinates.
+     */
+    public static void rotatePointF(PointF inOutPoint, @Rotation int rotation,
+            float parentW, float parentH) {
+        float origX = inOutPoint.x;
+        switch (rotation) {
+            case ROTATION_0:
+                return;
+            case ROTATION_90:
+                inOutPoint.x = inOutPoint.y;
+                inOutPoint.y = parentW - origX;
+                return;
+            case ROTATION_180:
+                inOutPoint.x = parentW - inOutPoint.x;
+                inOutPoint.y = parentH - inOutPoint.y;
+                return;
+            case ROTATION_270:
+                inOutPoint.x = parentH - inOutPoint.y;
+                inOutPoint.y = origX;
+        }
+    }
+
+    /**
      * Sets a matrix such that given a rotation, it transforms physical display
      * coordinates to that rotation's logical coordinates.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0f970bf..09a9d46 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -853,7 +853,12 @@
 
     private SurfaceSyncGroup mSyncGroup;
     private SurfaceSyncGroup.TransactionReadyCallback mTransactionReadyCallback;
-    private int mNumSyncsInProgress = 0;
+
+    private static final Object sSyncProgressLock = new Object();
+    // The count needs to be static since it's used to enable or disable RT animations which is
+    // done at a global level per process. If any VRI syncs are in progress, we can't enable RT
+    // animations until all are done.
+    private static int sNumSyncsInProgress = 0;
 
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
 
@@ -11229,13 +11234,6 @@
         });
     }
 
-    private final Executor mPostAtFrontExecutor = new Executor() {
-        @Override
-        public void execute(Runnable command) {
-            mHandler.postAtFrontOfQueue(command);
-        }
-    };
-
     public final SurfaceSyncGroup.SyncTarget mSyncTarget = new SurfaceSyncGroup.SyncTarget() {
         @Override
         public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
@@ -11245,9 +11243,6 @@
                 // Always sync the buffer if the sync request did not come from VRI.
                 mSyncBuffer = true;
             }
-            if (mAttachInfo.mThreadedRenderer != null) {
-                HardwareRenderer.setRtAnimationsEnabled(false);
-            }
 
             if (mTransactionReadyCallback != null) {
                 Log.d(mTag, "Already set sync for the next draw.");
@@ -11261,16 +11256,29 @@
                 scheduleTraversals();
             }
         }
+    };
 
-        private void updateSyncInProgressCount(SurfaceSyncGroup parentSyncGroup) {
-            mNumSyncsInProgress++;
-            parentSyncGroup.addSyncCompleteCallback(mPostAtFrontExecutor, () -> {
-                if (--mNumSyncsInProgress == 0 && mAttachInfo.mThreadedRenderer != null) {
+    private final Executor mSimpleExecutor = Runnable::run;
+
+    private void updateSyncInProgressCount(SurfaceSyncGroup syncGroup) {
+        if (mAttachInfo.mThreadedRenderer == null) {
+            return;
+        }
+
+        synchronized (sSyncProgressLock) {
+            if (sNumSyncsInProgress++ == 0) {
+                HardwareRenderer.setRtAnimationsEnabled(false);
+            }
+        }
+
+        syncGroup.addSyncCompleteCallback(mSimpleExecutor, () -> {
+            synchronized (sSyncProgressLock) {
+                if (--sNumSyncsInProgress == 0) {
                     HardwareRenderer.setRtAnimationsEnabled(true);
                 }
-            });
-        }
-    };
+            }
+        });
+    }
 
     @Override
     public SurfaceSyncGroup.SyncTarget getSyncTarget() {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 5265840..e6379cf 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -968,7 +968,8 @@
         final int position = getSelectedItemPosition();
         if (position >= 0) {
             // we fire selection events here not in View
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+            // posting the event should delay it long enough for UI changes to take effect.
+            post(() -> sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED));
         }
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5740f86..558d960 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2054,7 +2054,10 @@
         }
     }
 
-    void onDraw(Canvas canvas, Layout layout, Path highlight, Paint highlightPaint,
+    void onDraw(Canvas canvas, Layout layout,
+            List<Path> highlightPaths,
+            List<Paint> highlightPaints,
+            Path selectionHighlight, Paint selectionHighlightPaint,
             int cursorOffsetVertical) {
         final int selectionStart = mTextView.getSelectionStart();
         final int selectionEnd = mTextView.getSelectionEnd();
@@ -2078,37 +2081,40 @@
             mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
         }
 
-        if (highlight != null && selectionStart == selectionEnd && mDrawableForCursor != null) {
+        if (selectionHighlight != null && selectionStart == selectionEnd
+                && mDrawableForCursor != null) {
             drawCursor(canvas, cursorOffsetVertical);
             // Rely on the drawable entirely, do not draw the cursor line.
             // Has to be done after the IMM related code above which relies on the highlight.
-            highlight = null;
+            selectionHighlight = null;
         }
 
         if (mSelectionActionModeHelper != null) {
             mSelectionActionModeHelper.onDraw(canvas);
             if (mSelectionActionModeHelper.isDrawingHighlight()) {
-                highlight = null;
+                selectionHighlight = null;
             }
         }
 
         if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
-            drawHardwareAccelerated(canvas, layout, highlight, highlightPaint,
-                    cursorOffsetVertical);
+            drawHardwareAccelerated(canvas, layout, highlightPaths, highlightPaints,
+                    selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
         } else {
-            layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+            layout.draw(canvas, highlightPaths, highlightPaints, selectionHighlight,
+                    selectionHighlightPaint, cursorOffsetVertical);
         }
     }
 
-    private void drawHardwareAccelerated(Canvas canvas, Layout layout, Path highlight,
-            Paint highlightPaint, int cursorOffsetVertical) {
+    private void drawHardwareAccelerated(Canvas canvas, Layout layout,
+            List<Path> highlightPaths, List<Paint> highlightPaints,
+            Path selectionHighlight, Paint selectionHighlightPaint, int cursorOffsetVertical) {
         final long lineRange = layout.getLineRangeForDraw(canvas);
         int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
         int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
         if (lastLine < 0) return;
 
-        layout.drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,
-                firstLine, lastLine);
+        layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
+                selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
 
         if (layout instanceof DynamicLayout) {
             if (mTextRenderNodes == null) {
@@ -2154,8 +2160,9 @@
                     continue;
                 }
                 startIndexToFindAvailableRenderNode = drawHardwareAcceleratedInner(canvas, layout,
-                        highlight, highlightPaint, cursorOffsetVertical, blockEndLines,
-                        blockIndices, i, numberOfBlocks, startIndexToFindAvailableRenderNode);
+                        selectionHighlight, selectionHighlightPaint, cursorOffsetVertical,
+                        blockEndLines, blockIndices, i, numberOfBlocks,
+                        startIndexToFindAvailableRenderNode);
                 if (blockEndLines[i] >= lastLine) {
                     lastIndex = Math.max(indexFirstChangedBlock, i + 1);
                     break;
@@ -2169,9 +2176,9 @@
                             || mTextRenderNodes[blockIndex] == null
                             || mTextRenderNodes[blockIndex].needsToBeShifted) {
                         startIndexToFindAvailableRenderNode = drawHardwareAcceleratedInner(canvas,
-                                layout, highlight, highlightPaint, cursorOffsetVertical,
-                                blockEndLines, blockIndices, block, numberOfBlocks,
-                                startIndexToFindAvailableRenderNode);
+                                layout, selectionHighlight, selectionHighlightPaint,
+                                cursorOffsetVertical, blockEndLines, blockIndices, block,
+                                numberOfBlocks, startIndexToFindAvailableRenderNode);
                     }
                 }
             }
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index ba6fa19..ad431ef 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -802,6 +802,21 @@
         dialog.dismiss();
     }
 
+    /**
+     * Sets selection and dismisses the spinner's popup if it can be dismissed.
+     * For ease of use in tests, where publicly obtaining the spinner's popup is difficult.
+     *
+     * @param which index of the item to be selected.
+     * @hide
+     */
+    @TestApi
+    public void onClick(int which) {
+        setSelection(which);
+        if (mPopup != null && mPopup.isShowing()) {
+            mPopup.dismiss();
+        }
+    }
+
     @Override
     public CharSequence getAccessibilityClassName() {
         return Spinner.class.getName();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bf1a2bd..475a849 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -102,6 +102,7 @@
 import android.text.GetChars;
 import android.text.GraphemeClusterSegmentFinder;
 import android.text.GraphicsOperations;
+import android.text.Highlights;
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
@@ -238,6 +239,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
@@ -926,6 +928,12 @@
     @UnsupportedAppUsage
     private boolean mHighlightPathBogus = true;
 
+    private List<Path> mHighlightPaths;
+    private List<Paint> mHighlightPaints;
+    private Highlights mHighlights;
+    private final List<Path> mPathRecyclePool = new ArrayList<>();
+    private boolean mHighlightPathsBogus = true;
+
     // Although these fields are specific to editable text, they are not added to Editor because
     // they are defined by the TextView's style and are theme-dependent.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -6131,6 +6139,34 @@
     }
 
     /**
+     * Set Highlights
+     *
+     * @param highlights A highlight object. Call with null for reset.
+     *
+     * @see #getHighlights()
+     * @see Highlights
+     */
+    public void setHighlights(@Nullable Highlights highlights) {
+        mHighlights = highlights;
+        mHighlightPathsBogus = true;
+        invalidate();
+    }
+
+    /**
+     * Returns highlights
+     *
+     * @return a highlight to be drawn. null if no highlight was set.
+     *
+     * @see #setHighlights(Highlights)
+     * @see Highlights
+     *
+     */
+    @Nullable
+    public Highlights getHighlights() {
+        return mHighlights;
+    }
+
+    /**
      * Convenience method to append the specified text to the TextView's
      * display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE}
      * if it was not already editable.
@@ -8219,6 +8255,54 @@
         return drawableState;
     }
 
+    private void maybeUpdateHighlightPaths() {
+        if (!mHighlightPathsBogus) {
+            return;
+        }
+
+        if (mHighlightPaths != null) {
+            mPathRecyclePool.addAll(mHighlightPaths);
+            mHighlightPaths.clear();
+            mHighlightPaints.clear();
+        } else {
+            mHighlightPaths = new ArrayList<>();
+            mHighlightPaints = new ArrayList<>();
+        }
+
+        if (mHighlights != null) {
+            for (int i = 0; i < mHighlights.getSize(); ++i) {
+                final int[] ranges = mHighlights.getRanges(i);
+                final Paint paint = mHighlights.getPaint(i);
+
+                final Path path;
+                if (mPathRecyclePool.isEmpty()) {
+                    path = new Path();
+                } else {
+                    path = mPathRecyclePool.get(mPathRecyclePool.size() - 1);
+                    mPathRecyclePool.remove(mPathRecyclePool.size() - 1);
+                    path.reset();
+                }
+
+                boolean atLeastOnePathAdded = false;
+                for (int j = 0; j < ranges.length / 2; ++j) {
+                    final int start = ranges[2 * j];
+                    final int end = ranges[2 * j + 1];
+                    if (start < end) {
+                        mLayout.getSelection(start, end, (left, top, right, bottom, layout) ->
+                                path.addRect(left, top, right, bottom, Path.Direction.CW)
+                        );
+                        atLeastOnePathAdded = true;
+                    }
+                }
+                if (atLeastOnePathAdded) {
+                    mHighlightPaths.add(path);
+                    mHighlightPaints.add(paint);
+                }
+            }
+        }
+        mHighlightPathsBogus = false;
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private Path getUpdatedHighlightPath() {
         Path highlight = null;
@@ -8418,17 +8502,21 @@
 
         final int cursorOffsetVertical = voffsetCursor - voffsetText;
 
+        maybeUpdateHighlightPaths();
         Path highlight = getUpdatedHighlightPath();
         if (mEditor != null) {
-            mEditor.onDraw(canvas, layout, highlight, mHighlightPaint, cursorOffsetVertical);
+            mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
+                    mHighlightPaint, cursorOffsetVertical);
         } else {
-            layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
+            layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+                    cursorOffsetVertical);
         }
 
         if (mMarquee != null && mMarquee.shouldDrawGhost()) {
             final float dx = mMarquee.getGhostOffset();
             canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
-            layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
+            layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+                    cursorOffsetVertical);
         }
 
         canvas.restore();
@@ -9750,6 +9838,7 @@
         mOldMaxMode = mMaxMode;
 
         mHighlightPathBogus = true;
+        mHighlightPathsBogus = true;
 
         if (wantWidth < 0) {
             wantWidth = 0;
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 828e5cf..2d04bdb 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -140,7 +140,6 @@
 
         ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
 
-        boolean foundConfigFile = false;
         final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos =
                 packageProvider == null ? null : getOverlayPackageInfos(packageProvider);
 
@@ -154,7 +153,6 @@
                             activeApexesPerPartition.getOrDefault(partition.type,
                                     Collections.emptyList()));
             if (partitionOverlays != null) {
-                foundConfigFile = true;
                 overlays.addAll(partitionOverlays);
                 continue;
             }
@@ -191,12 +189,6 @@
             overlays.addAll(partitionConfigs);
         }
 
-        if (!foundConfigFile) {
-            // If no overlay configuration files exist, disregard partition precedence and allow
-            // android:priority to reorder overlays across partition boundaries.
-            overlays.sort(sStaticOverlayComparator);
-        }
-
         for (int i = 0, n = overlays.size(); i < n; i++) {
             // Add the configurations to a map so definitions of an overlay in an earlier
             // partition can be replaced by an overlay with the same package name in a later
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9e563de..6ceffde 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -645,7 +645,6 @@
     char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];
     char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
     char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
-    char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX];
     char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];
     char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX];
     char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
@@ -860,10 +859,7 @@
     parseRuntimeOption("dalvik.vm.jitpthreadpriority",
                        jitpthreadpriorityOptsBuf,
                        "-Xjitpthreadpriority:");
-    property_get("dalvik.vm.usejitprofiles", useJitProfilesOptsBuf, "");
-    if (strcmp(useJitProfilesOptsBuf, "true") == 0) {
-        addOption("-Xjitsaveprofilinginfo");
-    }
+    addOption("-Xjitsaveprofilinginfo");
 
     parseRuntimeOption("dalvik.vm.jitprithreadweight",
                        jitprithreadweightOptBuf,
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 29560dc..e9ada23 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -96,7 +96,7 @@
   }
 
   bool ForEachFile(const std::string& /* root_path */,
-                   const std::function<void(StringPiece, FileType)>& /* f */) const {
+                   android::base::function_ref<void(StringPiece, FileType)> /* f */) const {
     return true;
   }
 
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 03432e9..7f630cb 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -61,7 +61,8 @@
 
     viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
     viewport->isActive = env->GetBooleanField(viewportObj, gDisplayViewportClassInfo.isActive);
-    viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+    jint orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+    viewport->orientation = static_cast<ui::Rotation>(orientation);
     viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
     viewport->deviceHeight = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceHeight);
 
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 80df0ea..403c583 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -763,7 +763,12 @@
 
 static jint android_view_MotionEvent_nativeGetSurfaceRotation(jlong nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return jint(event->getSurfaceRotation());
+    auto rotation = event->getSurfaceRotation();
+    if (rotation) {
+        return static_cast<jint>(rotation.value());
+    } else {
+        return -1;
+    }
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 4650000..7393c6f 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -977,12 +977,11 @@
         optional int32 profile = 2;
     }
     repeated UserProfile user_profile_group_ids = 4;
-    repeated int32 visible_users_array = 5;
 
     // current_user contains the id of the current user, while current_profiles contains the ids of
     // both the current user and its profiles (if any)
-    optional int32 current_user = 6;
-    repeated int32 current_profiles = 7;
+    optional int32 current_user = 5;
+    repeated int32 current_profiles = 6;
 }
 
 // sync with com.android.server.am.AppTimeTracker.java
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 6d05e07..a2ef400 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -8,7 +8,6 @@
 hackbod@google.com
 ilyamaty@google.com
 jaggies@google.com
-jdemeulenaere@google.com
 jsharkey@android.com
 jsharkey@google.com
 juliacr@google.com
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb70344..abbff58 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3165,20 +3165,20 @@
         <attr name="canDisplayOnRemoteDevices" format="boolean"/>
         <attr name="allowUntrustedActivityEmbedding" />
         <attr name="knownActivityEmbeddingCerts" />
-        <!-- Specifies the category of the target display the activity is expected to run on. Upon
-             creation, a virtual display can specify which display categories it supports and one of
-             the category must be present in the activity's manifest to allow this activity to run.
-             The default value is {@code null}, which indicates the activity does not belong to a
-             restricted display category and thus can only run on a display that didn't specify any
-             display categories. Each activity can only specify one category it targets to but a
-             virtual display can accommodate multiple restricted categories.
+        <!-- Specifies the required display category of the activity. Upon creation, a display can
+             specify which display categories it supports and one of the categories must be present
+             in the {@code <activity>} element to allow this activity to run. The default value is
+             {@code null}, which indicates the activity does not have a required display category
+             and thus can only run on a display that didn't specify any display categories. Each
+             activity can only specify one required category but a display can accommodate multiple
+             display categories.
 
              <p> This field should be formatted as a Java-language-style free form string(for
              example, com.google.automotive_entertainment), which may contain uppercase or lowercase
              letters ('A' through 'Z'), numbers, and underscores ('_') but may only start with
              letters.
          -->
-        <attr name="targetDisplayCategory" format="string"/>
+        <attr name="requiredDisplayCategory" format="string"/>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2ab5b75..9a585a1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2078,6 +2078,13 @@
     <!-- Flag indicating whether the current device supports "Ask every time" for sms-->
     <bool name="config_sms_ask_every_time_support">true</bool>
 
+    <!-- Flag indicating whether the current device allows acknowledgement of SIM operation like
+         SM-PP or saving SMS to SIM can be done via the IMS interfaces.
+         If true,this means that the device supports sending of sim operation response via the
+         IMS interface APIs. This can be overridden to false for devices which can't send
+         sim operation acknowledgements via IMS interface APIs. -->
+    <bool name="config_smppsim_response_via_ims">false</bool>
+
     <!-- Flag indicating whether the current device allows data.
          If true, this means that the device supports data connectivity through
          the telephony network.
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index bc5878a..a9bec7a9 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -116,7 +116,7 @@
     <public name="handwritingBoundsOffsetBottom" />
     <public name="accessibilityDataPrivate" />
     <public name="enableTextStylingShortcuts" />
-    <public name="targetDisplayCategory"/>
+    <public name="requiredDisplayCategory"/>
     <public name="maxConcurrentSessionsCount" />
   </staging-public-group>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 168806a..ace7e4c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -315,6 +315,7 @@
   <java-symbol type="bool" name="config_sms_ask_every_time_support" />
   <java-symbol type="bool" name="config_sms_capable" />
   <java-symbol type="bool" name="config_sms_utf8_support" />
+  <java-symbol type="bool" name="config_smppsim_response_via_ims" />
   <java-symbol type="bool" name="config_mobile_data_capable" />
   <java-symbol type="bool" name="config_suspendWhenScreenOffDueToProximity" />
   <java-symbol type="bool" name="config_swipeDisambiguation" />
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
index 36aa915..1cc0a985 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
@@ -210,9 +210,9 @@
                 any(IServiceCallback.class)));
 
         doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
+                eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
         doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
+                eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
 
         when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
         when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index 7a8475f..a034653 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -58,14 +58,13 @@
     @Mock
     private android.hardware.broadcastradio.ICloseHandle mHalCloseHandleMock;
 
-    private final Object mLock = new Object();
     // RadioModule under test
     private RadioModule mRadioModule;
     private android.hardware.broadcastradio.IAnnouncementListener mHalListener;
 
     @Before
     public void setup() throws RemoteException {
-        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
+        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
 
         // TODO(b/241118988): test non-null image for getImage method
         when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 87d0ea4..993ca77 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -83,7 +83,6 @@
     @Mock private IBroadcastRadio mBroadcastRadioMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
 
-    private final Object mLock = new Object();
     // RadioModule under test
     private RadioModule mRadioModule;
 
@@ -104,7 +103,7 @@
         doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
 
         mRadioModule = new RadioModule(mBroadcastRadioMock,
-                AidlTestUtils.makeDefaultModuleProperties(), mLock);
+                AidlTestUtils.makeDefaultModuleProperties());
 
         doAnswer(invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
index 0b7bbea..c9224bf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
@@ -59,8 +59,6 @@
             new ArrayList<>(Arrays.asList("FmService", "DabService"));
     private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
 
-    private final Object mLock = new Object();
-
     private BroadcastRadioService mBroadcastRadioService;
     private DeathRecipient mFmDeathRecipient;
 
@@ -200,7 +198,7 @@
 
         mockServiceManager();
         mBroadcastRadioService = new BroadcastRadioService(/* nextModuleId= */ FM_RADIO_MODULE_ID,
-                mLock, mServiceManagerMock);
+                mServiceManagerMock);
     }
 
     private void mockServiceManager() throws RemoteException {
@@ -221,9 +219,9 @@
                 }).thenReturn(true);
 
         doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(FM_RADIO_MODULE_ID), anyString(), any(Object.class)));
+                eq(FM_RADIO_MODULE_ID), anyString()));
         doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(DAB_RADIO_MODULE_ID), anyString(), any(Object.class)));
+                eq(DAB_RADIO_MODULE_ID), anyString()));
 
         when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
         when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
index 48f5a46..1f5e770 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
@@ -62,13 +62,12 @@
     @Mock
     private android.hardware.broadcastradio.V2_0.ICloseHandle mHalCloseHandleMock;
 
-    private final Object mLock = new Object();
     private RadioModule mRadioModule;
     private android.hardware.broadcastradio.V2_0.IAnnouncementListener mHalListener;
 
     @Before
     public void setup() throws RemoteException {
-        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
+        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
 
         when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index e3c9faa..7d604d4 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -64,7 +64,6 @@
     @Mock ITunerSession mHalTunerSessionMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
 
-    private final Object mLock = new Object();
     // RadioModule under test
     private RadioModule mRadioModule;
 
@@ -100,7 +99,7 @@
         doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
 
         mRadioModule = new RadioModule(mBroadcastRadioMock,
-                TestUtils.makeDefaultModuleProperties(), mLock);
+                TestUtils.makeDefaultModuleProperties());
 
         doAnswer((Answer) invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index b7da5d0..ff988a2 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -84,7 +84,6 @@
             new RadioManager.FmBandConfig(FM_BAND_DESCRIPTOR);
     private static final int UNSUPPORTED_CONFIG_FLAG = 0;
 
-    private final Object mLock = new Object();
     private final ArrayMap<Integer, Boolean> mHalConfigMap = new ArrayMap<>();
     private RadioModule mRadioModule;
     private ITunerCallback mHalTunerCallback;
@@ -105,7 +104,7 @@
         doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
 
         mRadioModule = new RadioModule(mBroadcastRadioMock,
-                TestUtils.makeDefaultModuleProperties(), mLock);
+                TestUtils.makeDefaultModuleProperties());
 
         doAnswer(invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index cfca037..625c318 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -18,8 +18,12 @@
 
 import androidx.core.util.forEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.math.ceil
+import kotlin.math.floor
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -72,7 +76,70 @@
         }
     }
 
+    @LargeTest
+    @Test
+    fun allFeasibleScalesAndConversionsDoNotCrash() {
+        generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+            .mapNotNull{ FontScaleConverterFactory.forScale(it) }
+            .flatMap{ table ->
+                generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+                    .map{ Pair(table, it) }
+            }
+            .forEach { (table, sp) ->
+                try {
+                    assertWithMessage(
+                        "convertSpToDp(%s) on table: %s",
+                        sp.toString(),
+                        table.toString()
+                    )
+                        .that(table.convertSpToDp(sp))
+                        .isFinite()
+                } catch (e: Exception) {
+                    throw AssertionError("Exception during convertSpToDp($sp) on table: $table", e)
+                }
+            }
+    }
+
+    @Test
+    fun testGenerateSequenceOfFractions() {
+        val fractions = generateSequenceOfFractions(-1000f..1000f, step = 0.1f)
+            .toList()
+        fractions.forEach {
+            assertThat(it).isAtLeast(-1000f)
+            assertThat(it).isAtMost(1000f)
+        }
+
+        assertThat(fractions).isInStrictOrder()
+        assertThat(fractions).hasSize(1000 * 2 * 10 + 1) // Don't forget the 0 in the middle!
+
+        assertThat(fractions).contains(100f)
+        assertThat(fractions).contains(500.1f)
+        assertThat(fractions).contains(500.2f)
+        assertThat(fractions).contains(0.2f)
+        assertThat(fractions).contains(0f)
+        assertThat(fractions).contains(-10f)
+        assertThat(fractions).contains(-10f)
+        assertThat(fractions).contains(-10.3f)
+
+        assertThat(fractions).doesNotContain(-10.31f)
+        assertThat(fractions).doesNotContain(0.35f)
+        assertThat(fractions).doesNotContain(0.31f)
+        assertThat(fractions).doesNotContain(-.35f)
+    }
+
     companion object {
         private const val CONVERSION_TOLERANCE = 0.05f
     }
 }
+
+fun generateSequenceOfFractions(
+    range: ClosedFloatingPointRange<Float>,
+    step: Float
+): Sequence<Float> {
+    val multiplier = 1f / step
+    val start = floor(range.start * multiplier).toInt()
+    val endInclusive = ceil(range.endInclusive * multiplier).toInt()
+    return generateSequence(start) { it + 1 }
+        .takeWhile { it <= endInclusive }
+        .map{ it.toFloat() / multiplier }
+}
diff --git a/core/tests/coretests/src/android/util/RotationUtilsTest.java b/core/tests/coretests/src/android/util/RotationUtilsTest.java
index 826eb30..1b1ee4f 100644
--- a/core/tests/coretests/src/android/util/RotationUtilsTest.java
+++ b/core/tests/coretests/src/android/util/RotationUtilsTest.java
@@ -18,6 +18,7 @@
 
 import static android.util.RotationUtils.rotateBounds;
 import static android.util.RotationUtils.rotatePoint;
+import static android.util.RotationUtils.rotatePointF;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
@@ -25,6 +26,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -79,4 +81,26 @@
         rotatePoint(testResult, ROTATION_270, parentW, parentH);
         assertEquals(new Point(560, 60), testResult);
     }
+
+    @Test
+    public void testRotatePointF() {
+        float parentW = 1000f;
+        float parentH = 600f;
+        PointF testPt = new PointF(60f, 40f);
+
+        PointF testResult = new PointF(testPt);
+        rotatePointF(testResult, ROTATION_90, parentW, parentH);
+        assertEquals(40f, testResult.x, .1f);
+        assertEquals(940f, testResult.y, .1f);
+
+        testResult.set(testPt.x, testPt.y);
+        rotatePointF(testResult, ROTATION_180, parentW, parentH);
+        assertEquals(940f, testResult.x, .1f);
+        assertEquals(560f, testResult.y, .1f);
+
+        testResult.set(testPt.x, testPt.y);
+        rotatePointF(testResult, ROTATION_270, parentW, parentH);
+        assertEquals(560f, testResult.x, .1f);
+        assertEquals(60f, testResult.y, .1f);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
index caec365..0f30cfe 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
@@ -286,6 +286,39 @@
     }
 
     @Test
+    public void testPartialConfigPartitionPrecedence() throws IOException {
+        createFile("/odm/overlay/config/config.xml",
+                "<config>"
+                        + "  <overlay package=\"two\" enabled=\"true\" />"
+                        + "</config>");
+
+        mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+                1);
+        mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two");
+        mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+                true, 0);
+
+        final OverlayConfig overlayConfig = createConfigImpl();
+        assertConfig(overlayConfig, "one", false, true, 0);
+        assertConfig(overlayConfig, "two", true, true, 1);
+        assertConfig(overlayConfig, "three", false, true, 2);
+    }
+
+    @Test
+    public void testNoConfigPartitionPrecedence() throws IOException {
+        mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+                1);
+        mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two", "android", 0, true, 2);
+        mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+                true, 0);
+
+        final OverlayConfig overlayConfig = createConfigImpl();
+        assertConfig(overlayConfig, "one", false, true, 0);
+        assertConfig(overlayConfig, "two", false, true, 1);
+        assertConfig(overlayConfig, "three", false, true, 2);
+    }
+
+    @Test
     public void testImmutable() throws IOException {
         createFile("/product/overlay/config/config.xml",
                 "<config>"
@@ -507,37 +540,6 @@
     }
 
     @Test
-    public void testNoConfigsAllowPartitionReordering() throws IOException {
-        mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
-                1);
-        mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
-                0);
-
-        final OverlayConfig overlayConfig = createConfigImpl();
-        assertConfig(overlayConfig, "one", false, true, 1);
-        assertConfig(overlayConfig, "two", false, true, 0);
-    }
-
-    @Test
-    public void testConfigDisablesPartitionReordering() throws IOException {
-        createFile("/odm/overlay/config/config.xml",
-                "<config>"
-                        + "  <overlay package=\"two\" enabled=\"true\" />"
-                        + "</config>");
-
-        mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
-                1);
-        mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two");
-        mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
-                true, 0);
-
-        final OverlayConfig overlayConfig = createConfigImpl();
-        assertConfig(overlayConfig, "one", false, true, 0);
-        assertConfig(overlayConfig, "two", true, true, 1);
-        assertConfig(overlayConfig, "three", false, true, 2);
-    }
-
-    @Test
     public void testStaticOverlayOutsideOverlayDir() throws IOException {
         mScannerRule.addOverlay(createFile("/product/app/one.apk"), "one", "android", 0, true, 0);
 
@@ -550,7 +552,7 @@
     @Test
     public void testSortStaticOverlaysDifferentTargets() throws IOException {
         mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "other", 0, true, 0);
-        mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+        mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true,
                 0);
 
         final OverlayConfig overlayConfig = createConfigImpl();
@@ -559,15 +561,33 @@
     }
 
     @Test
+    public void testSortStaticOverlaysDifferentPartitions() throws IOException {
+        mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+                2);
+        mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true,
+                3);
+        mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+                true, 0);
+        mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android", 0,
+                true, 1);
+
+        final OverlayConfig overlayConfig = createConfigImpl();
+        assertConfig(overlayConfig, "one", false, true, 0);
+        assertConfig(overlayConfig, "two", false, true, 1);
+        assertConfig(overlayConfig, "three", false, true, 2);
+        assertConfig(overlayConfig, "four", false, true, 3);
+    }
+
+    @Test
     public void testSortStaticOverlaysSamePriority() throws IOException {
         mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
                 0);
-        mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+        mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true,
                 0);
 
         final OverlayConfig overlayConfig = createConfigImpl();
-        assertConfig(overlayConfig, "one", false, true, 1);
-        assertConfig(overlayConfig, "two", false, true, 0);
+        assertConfig(overlayConfig, "one", false, true, 0);
+        assertConfig(overlayConfig, "two", false, true, 1);
     }
 
     @Test
diff --git a/core/tests/fuzzers/FuzzService/Android.bp b/core/tests/fuzzers/FuzzService/Android.bp
new file mode 100644
index 0000000..5093185
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/Android.bp
@@ -0,0 +1,28 @@
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+    name: "random_parcel_lib",
+    srcs: ["FuzzBinder.java"],
+}
+
+cc_library_shared {
+    name: "librandom_parcel_jni",
+    defaults: ["service_fuzzer_defaults"],
+    srcs: [
+        "random_parcel_jni.cpp",
+    ],
+    shared_libs: [
+        "libandroid_runtime",
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libnativehelper_lazy",
+        "libbinder_random_parcel",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/core/tests/fuzzers/FuzzService/FuzzBinder.java b/core/tests/fuzzers/FuzzService/FuzzBinder.java
new file mode 100644
index 0000000..7096f52
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/FuzzBinder.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package randomparcel;
+import android.os.IBinder;
+
+public class FuzzBinder {
+    static {
+        System.loadLibrary("random_parcel_jni");
+    }
+
+    // DO NOT REUSE: This API should be called from fuzzer to setup JNI dependencies from
+    // libandroid_runtime. THIS IS WORKAROUND. Please file a bug if you need to use this.
+    public static void init() {
+        System.loadLibrary("android_runtime");
+        registerNatives();
+    }
+
+    // This API automatically fuzzes provided service
+    public static void fuzzService(IBinder binder, byte[] data) {
+        fuzzServiceInternal(binder, data);
+    }
+
+    private static native void fuzzServiceInternal(IBinder binder, byte[] data);
+    private static native int registerNatives();
+}
diff --git a/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp b/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp
new file mode 100644
index 0000000..c0528d5
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "random_parcel_jni.h"
+#include <android_util_Binder.h>
+#include <fuzzbinder/libbinder_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+using namespace android;
+
+// JNI interface for fuzzService
+JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_fuzzServiceInternal(JNIEnv *env, jobject thiz, jobject javaBinder, jbyteArray fuzzData) {
+    size_t len = static_cast<size_t>(env->GetArrayLength(fuzzData));
+    uint8_t data[len];
+    env->GetByteArrayRegion(fuzzData, 0, len, reinterpret_cast<jbyte*>(data));
+
+    FuzzedDataProvider provider(data, len);
+    sp<IBinder> binder = android::ibinderForJavaObject(env, javaBinder);
+    fuzzService(binder, std::move(provider));
+}
+
+// API used by AIDL fuzzers to access JNI functions from libandroid_runtime.
+JNIEXPORT jint JNICALL Java_randomparcel_FuzzBinder_registerNatives(JNIEnv* env) {
+    return registerFrameworkNatives(env);
+}
diff --git a/core/tests/fuzzers/FuzzService/random_parcel_jni.h b/core/tests/fuzzers/FuzzService/random_parcel_jni.h
new file mode 100644
index 0000000..20a4c9d
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/random_parcel_jni.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <jni.h>
+
+extern "C" {
+    JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_fuzzServiceInternal(JNIEnv *env, jobject thiz, jobject javaBinder, jbyteArray fuzzData);
+
+    // Function to register libandroid_runtime JNI functions with java env.
+    JNIEXPORT jint JNICALL Java_randomparcel_FuzzBinder_registerNatives(JNIEnv* env);
+
+    // Function from AndroidRuntime
+    jint registerFrameworkNatives(JNIEnv* env);
+}
diff --git a/core/tests/fuzzers/OWNERS b/core/tests/fuzzers/OWNERS
new file mode 100644
index 0000000..b972ac0
--- /dev/null
+++ b/core/tests/fuzzers/OWNERS
@@ -0,0 +1,2 @@
+smoreland@google.com
+waghpawan@google.com
diff --git a/core/tests/fuzzers/java_service_fuzzer/Android.bp b/core/tests/fuzzers/java_service_fuzzer/Android.bp
new file mode 100644
index 0000000..625de14
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+aidl_interface {
+    name: "fuzzTestInterface",
+    srcs: ["fuzztest/ITestService.aidl"],
+    unstable: true,
+    backend: {
+        java: {
+            enabled: true,
+        },
+    },
+}
+
+java_fuzz {
+    name: "java_binder_service_fuzzer",
+    srcs: [
+        "ServiceFuzzer.java",
+        "TestService.java",
+        ":framework-core-sources-for-fuzzers",
+    ],
+    static_libs: [
+        "jazzer",
+        "fuzzTestInterface-java",
+        "random_parcel_lib",
+    ],
+    jni_libs: [
+        "librandom_parcel_jni",
+        "libc++",
+        "libandroid_runtime",
+    ],
+    libs: [
+        "framework",
+        "unsupportedappusage",
+        "ext",
+        "framework-res",
+    ],
+    native_bridge_supported: true,
+}
diff --git a/core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java b/core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java
new file mode 100644
index 0000000..a6e0986
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+
+import randomparcel.FuzzBinder;
+
+public class ServiceFuzzer {
+
+    static {
+        // Initialize fuzzService and JNI dependencies
+        FuzzBinder.init();
+    }
+
+    public static void fuzzerTestOneInput(FuzzedDataProvider data) {
+        TestService service = new TestService();
+        FuzzBinder.fuzzService(service, data.consumeRemainingAsBytes());
+    }
+}
diff --git a/core/tests/fuzzers/java_service_fuzzer/TestService.java b/core/tests/fuzzers/java_service_fuzzer/TestService.java
new file mode 100644
index 0000000..4404386
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/TestService.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import fuzztest.ITestService;
+
+public class TestService extends ITestService.Stub {
+
+    @Override
+    public boolean repeatData(boolean token) {
+        return token;
+    }
+}
diff --git a/core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl b/core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl
new file mode 100644
index 0000000..b766c9f
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fuzztest;
+
+interface ITestService {
+    boolean repeatData(boolean token);
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1fd91de..9674b69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -475,8 +475,13 @@
 
     @VisibleForTesting
     public void onStatusBarStateChanged(boolean isShade) {
+        boolean didChange = mIsStatusBarShade != isShade;
+        if (DEBUG_BUBBLE_CONTROLLER) {
+            Log.d(TAG, "onStatusBarStateChanged isShade=" + isShade + " didChange=" + didChange);
+        }
         mIsStatusBarShade = isShade;
-        if (!mIsStatusBarShade) {
+        if (!mIsStatusBarShade && didChange) {
+            // Only collapse stack on change
             collapseStack();
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index e8b0f02..214b304 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -219,7 +219,11 @@
                 insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
         // Only insets the divider bar with task bar when it's expanded so that the rounded corners
         // will be drawn against task bar.
-        if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+        // But there is no need to do it when IME showing because there are no rounded corners at
+        // the bottom. This also avoids the problem of task bar height not changing when IME
+        // floating.
+        if (!insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME)
+                && taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
             mTempRect.inset(taskBarInsetsSource.calculateVisibleInsets(mTempRect));
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index ae49616..45b234a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -122,6 +122,7 @@
     private int mDensity;
 
     private final boolean mDimNonImeSide;
+    private ValueAnimator mDividerFlingAnimator;
 
     public SplitLayout(String windowName, Context context, Configuration configuration,
             SplitLayoutHandler splitLayoutHandler,
@@ -395,6 +396,10 @@
         mSplitWindowManager.release(t);
         mDisplayImeController.removePositionProcessor(mImePositionProcessor);
         mImePositionProcessor.reset();
+        if (mDividerFlingAnimator != null) {
+            mDividerFlingAnimator.cancel();
+        }
+        resetDividerPosition();
     }
 
     public void release() {
@@ -577,13 +582,18 @@
                     CUJ_SPLIT_SCREEN_RESIZE);
             return;
         }
-        ValueAnimator animator = ValueAnimator
+
+        if (mDividerFlingAnimator != null) {
+            mDividerFlingAnimator.cancel();
+        }
+
+        mDividerFlingAnimator = ValueAnimator
                 .ofInt(from, to)
                 .setDuration(duration);
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        animator.addUpdateListener(
+        mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mDividerFlingAnimator.addUpdateListener(
                 animation -> updateDivideBounds((int) animation.getAnimatedValue()));
-        animator.addListener(new AnimatorListenerAdapter() {
+        mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (flingFinishedCallback != null) {
@@ -591,14 +601,15 @@
                 }
                 InteractionJankMonitorUtils.endTracing(
                         CUJ_SPLIT_SCREEN_RESIZE);
+                mDividerFlingAnimator = null;
             }
 
             @Override
             public void onAnimationCancel(Animator animation) {
-                setDividePosition(to, true /* applyLayoutChange */);
+                mDividerFlingAnimator = null;
             }
         });
-        animator.start();
+        mDividerFlingAnimator.start();
     }
 
     /** Switch both surface position with animation. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 90b35a5..44bcdb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -82,7 +82,7 @@
         mTasks.put(taskInfo.taskId, state);
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-            mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
+            mWindowDecorationViewModel.onTaskOpening(taskInfo, leash, t, t);
             t.apply();
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 168f6d7..6e710f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -120,7 +120,7 @@
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mWindowDecorViewModel.createWindowDecoration(
+        mWindowDecorViewModel.onTaskOpening(
                 change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
@@ -128,31 +128,23 @@
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mWindowDecorViewModel.setupWindowDecorationForTransition(
-                change.getTaskInfo(), startT, finishT);
+        mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
     }
 
     private void onChangeTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        mWindowDecorViewModel.setupWindowDecorationForTransition(
-                change.getTaskInfo(), startT, finishT);
+        mWindowDecorViewModel.onTaskChanging(
+                change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
     private void onToFrontTransitionReady(
             TransitionInfo.Change change,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
-        boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition(
-                change.getTaskInfo(),
-                startT,
-                finishT);
-        if (!exists) {
-            // Window caption does not exist, create it
-            mWindowDecorViewModel.createWindowDecoration(
-                    change.getTaskInfo(), change.getLeash(), startT, finishT);
-        }
+        mWindowDecorViewModel.onTaskChanging(
+                change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
     @Override
@@ -188,4 +180,4 @@
             mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
         }
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 75a4091..6623f5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -103,7 +103,7 @@
         if (mWindowDecorViewModelOptional.isPresent()) {
             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
             createdWindowDecor = mWindowDecorViewModelOptional.get()
-                    .createWindowDecoration(taskInfo, leash, t, t);
+                    .onTaskOpening(taskInfo, leash, t, t);
             t.apply();
         }
         if (!createdWindowDecor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 17d7f5d..5376ae3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -97,6 +97,8 @@
     private int mShelfHeight;
     /** Whether the user has resized the PIP manually. */
     private boolean mHasUserResizedPip;
+    /** Whether the user has moved the PIP manually. */
+    private boolean mHasUserMovedPip;
     /**
      * Areas defined by currently visible apps that they prefer to keep clear from overlays such as
      * the PiP. Restricted areas may only move the PiP a limited amount from its anchor position.
@@ -279,6 +281,7 @@
         if (changed) {
             clearReentryState();
             setHasUserResizedPip(false);
+            setHasUserMovedPip(false);
         }
     }
 
@@ -442,6 +445,16 @@
         mHasUserResizedPip = hasUserResizedPip;
     }
 
+    /** Returns whether the user has moved the PIP. */
+    public boolean hasUserMovedPip() {
+        return mHasUserMovedPip;
+    }
+
+    /** Set whether the user has moved the PIP. */
+    public void setHasUserMovedPip(boolean hasUserMovedPip) {
+        mHasUserMovedPip = hasUserMovedPip;
+    }
+
     /**
      * Registers a callback when the minimal size of PIP that is set by the app changes.
      */
@@ -577,6 +590,8 @@
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
         pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
+        pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip);
+        pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip);
         if (mPipReentryState == null) {
             pw.println(innerPrefix + "mPipReentryState=null");
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 84071e0..690505e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.SystemProperties;
 import android.util.ArraySet;
 import android.view.Gravity;
 
@@ -34,6 +35,10 @@
  */
 public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
 
+    private boolean mKeepClearAreaGravityEnabled =
+            SystemProperties.getBoolean(
+                    "persist.wm.debug.enable_pip_keep_clear_algorithm_gravity", false);
+
     protected int mKeepClearAreasPadding;
 
     public PhonePipKeepClearAlgorithm(Context context) {
@@ -53,31 +58,36 @@
         Rect startingBounds = pipBoundsState.getBounds().isEmpty()
                 ? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
                 : pipBoundsState.getBounds();
-        float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
-        int verticalGravity = Gravity.BOTTOM;
-        int horizontalGravity;
-        if (snapFraction >= 0.5f && snapFraction < 2.5f) {
-            horizontalGravity = Gravity.RIGHT;
-        } else {
-            horizontalGravity = Gravity.LEFT;
-        }
-        // push the bounds based on the gravity
         Rect insets = new Rect();
         pipBoundsAlgorithm.getInsetBounds(insets);
         if (pipBoundsState.isImeShowing()) {
             insets.bottom -= pipBoundsState.getImeHeight();
         }
-        Rect pushedBounds = new Rect(startingBounds);
-        if (verticalGravity == Gravity.BOTTOM) {
-            pushedBounds.offsetTo(pushedBounds.left,
-                    insets.bottom - pushedBounds.height());
+        Rect pipBounds = new Rect(startingBounds);
+
+        // move PiP towards corner if user hasn't moved it manually or the flag is on
+        if (mKeepClearAreaGravityEnabled
+                || (!pipBoundsState.hasUserMovedPip() && !pipBoundsState.hasUserResizedPip())) {
+            float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
+            int verticalGravity = Gravity.BOTTOM;
+            int horizontalGravity;
+            if (snapFraction >= 0.5f && snapFraction < 2.5f) {
+                horizontalGravity = Gravity.RIGHT;
+            } else {
+                horizontalGravity = Gravity.LEFT;
+            }
+            if (verticalGravity == Gravity.BOTTOM) {
+                pipBounds.offsetTo(pipBounds.left,
+                        insets.bottom - pipBounds.height());
+            }
+            if (horizontalGravity == Gravity.RIGHT) {
+                pipBounds.offsetTo(insets.right - pipBounds.width(), pipBounds.top);
+            } else {
+                pipBounds.offsetTo(insets.left, pipBounds.top);
+            }
         }
-        if (horizontalGravity == Gravity.RIGHT) {
-            pushedBounds.offsetTo(insets.right - pushedBounds.width(), pushedBounds.top);
-        } else {
-            pushedBounds.offsetTo(insets.left, pushedBounds.top);
-        }
-        return findUnoccludedPosition(pushedBounds, pipBoundsState.getRestrictedKeepClearAreas(),
+
+        return findUnoccludedPosition(pipBounds, pipBoundsState.getRestrictedKeepClearAreas(),
                 pipBoundsState.getUnrestrictedKeepClearAreas(), insets);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index d28a9f3..efe938f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -612,12 +612,21 @@
                 new DisplayInsetsController.OnInsetsChangedListener() {
                     @Override
                     public void insetsChanged(InsetsState insetsState) {
+                        DisplayLayout pendingLayout =
+                                mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId());
+                        if (mIsInFixedRotation
+                                || pendingLayout.rotation()
+                                != mPipBoundsState.getDisplayLayout().rotation()) {
+                            // bail out if there is a pending rotation or fixed rotation change
+                            return;
+                        }
                         int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
                         onDisplayChanged(
                                 mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
                                 false /* saveRestoreSnapFraction */);
                         int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
                         if (!mEnablePipKeepClearAlgorithm) {
+                            // offset PiP to adjust for bottom inset change
                             int pipTop = mPipBoundsState.getBounds().top;
                             int diff = newMaxMovementBound - oldMaxMovementBound;
                             if (diff < 0 && pipTop > newMaxMovementBound) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a9a97be..83bc7c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -875,6 +875,8 @@
             }
 
             if (touchState.isDragging()) {
+                mPipBoundsState.setHasUserMovedPip(true);
+
                 // Move the pinned stack freely
                 final PointF lastDelta = touchState.getLastTouchDelta();
                 float lastX = mStartPosition.x + mDelta.x;
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 acb71a8..4cb7623 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
@@ -669,6 +669,12 @@
         mSplitLayout.init();
         mSplitLayout.setDivideRatio(splitRatio);
 
+        // Apply surface bounds before animation start.
+        SurfaceControl.Transaction startT = mTransactionPool.acquire();
+        updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
+        startT.apply();
+        mTransactionPool.release(startT);
+
         // Set false to avoid record new bounds with old task still on top;
         mShouldUpdateRecents = false;
         mIsDividerRemoteAnimating = true;
@@ -742,7 +748,6 @@
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t -> {
             setDividerVisibility(true, t);
-            updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
         });
 
         setEnterInstanceId(instanceId);
@@ -1035,7 +1040,7 @@
         mIsDividerRemoteAnimating = false;
 
         mSplitLayout.getInvisibleBounds(mTempRect1);
-        if (childrenToTop == null) {
+        if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
             mSideStage.removeAllTasks(wct, false /* toTop */);
             mMainStage.deactivate(wct, false /* toTop */);
             wct.reorder(mRootTaskInfo.token, false /* onTop */);
@@ -1294,13 +1299,6 @@
         }
     }
 
-    private void onStageChildTaskEnterPip() {
-        // When the exit split-screen is caused by one of the task enters auto pip,
-        // we want both tasks to be put to bottom instead of top, otherwise it will end up
-        // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
-        exitSplitScreen(null, EXIT_REASON_CHILD_TASK_ENTER_PIP);
-    }
-
     private void updateRecentTasksSplitPair() {
         if (!mShouldUpdateRecents) {
             return;
@@ -2063,7 +2061,6 @@
             // Update divider state after animation so that it is still around and positioned
             // properly for the animation itself.
             mSplitLayout.release();
-            mSplitLayout.resetDividerPosition();
             mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
         }
     }
@@ -2340,11 +2337,6 @@
         }
 
         @Override
-        public void onChildTaskEnterPip() {
-            StageCoordinator.this.onStageChildTaskEnterPip();
-        }
-
-        @Override
         public void onRootTaskVanished() {
             reset();
             StageCoordinator.this.onRootTaskVanished();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index bcf900b..358f712 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
@@ -74,8 +73,6 @@
 
         void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
 
-        void onChildTaskEnterPip();
-
         void onRootTaskVanished();
 
         void onNoLongerSupportMultiWindow();
@@ -257,9 +254,6 @@
                 // Status is managed/synchronized by the transition lifecycle.
                 return;
             }
-            if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
-                mCallbacks.onChildTaskEnterPip();
-            }
             sendStatusChanged();
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 8369569..e40db4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -55,6 +55,8 @@
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.transition.Transitions;
 
+import java.util.function.Supplier;
+
 /**
  * View model for the window decoration with a caption and shadows. Works with
  * {@link CaptionWindowDecoration}.
@@ -62,6 +64,8 @@
 
 public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
     private static final String TAG = "CaptionViewModel";
+    private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+    private final Supplier<InputManager> mInputManagerSupplier;
     private final ActivityTaskManager mActivityTaskManager;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final Context mContext;
@@ -77,6 +81,7 @@
 
     private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
     private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
+    private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory();
 
     public CaptionWindowDecorViewModel(
             Context context,
@@ -86,6 +91,29 @@
             DisplayController displayController,
             SyncTransactionQueue syncQueue,
             DesktopModeController desktopModeController) {
+        this(
+                context,
+                mainHandler,
+                mainChoreographer,
+                taskOrganizer,
+                displayController,
+                syncQueue,
+                desktopModeController,
+                new CaptionWindowDecoration.Factory(),
+                InputManager::getInstance);
+    }
+
+    public CaptionWindowDecorViewModel(
+            Context context,
+            Handler mainHandler,
+            Choreographer mainChoreographer,
+            ShellTaskOrganizer taskOrganizer,
+            DisplayController displayController,
+            SyncTransactionQueue syncQueue,
+            DesktopModeController desktopModeController,
+            CaptionWindowDecoration.Factory captionWindowDecorFactory,
+            Supplier<InputManager> inputManagerSupplier) {
+
         mContext = context;
         mMainHandler = mainHandler;
         mMainChoreographer = mainChoreographer;
@@ -94,7 +122,13 @@
         mDisplayController = displayController;
         mSyncQueue = syncQueue;
         mDesktopModeController = desktopModeController;
-        mTransitionDragActive = false;
+
+        mCaptionWindowDecorFactory = captionWindowDecorFactory;
+        mInputManagerSupplier = inputManagerSupplier;
+    }
+
+    void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) {
+        mEventReceiverFactory = eventReceiverFactory;
     }
 
     @Override
@@ -103,42 +137,13 @@
     }
 
     @Override
-    public boolean createWindowDecoration(
+    public boolean onTaskOpening(
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
         if (!shouldShowWindowDecor(taskInfo)) return false;
-        CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
-        if (oldDecoration != null) {
-            // close the old decoration if it exists to avoid two window decorations being added
-            oldDecoration.close();
-        }
-        final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
-                mContext,
-                mDisplayController,
-                mTaskOrganizer,
-                taskInfo,
-                taskSurface,
-                mMainHandler,
-                mMainChoreographer,
-                mSyncQueue);
-        mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
-
-        TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration,
-                mDragStartListener);
-        CaptionTouchEventListener touchEventListener =
-                new CaptionTouchEventListener(taskInfo, taskPositioner,
-                        windowDecoration.getDragDetector());
-        windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
-        windowDecoration.setDragResizeCallback(taskPositioner);
-        setupWindowDecorationForTransition(taskInfo, startT, finishT);
-        if (mInputMonitor == null) {
-            mInputMonitor = InputManager.getInstance().monitorGestureInput(
-                    "caption-touch", mContext.getDisplayId());
-            mEventReceiver = new EventReceiver(
-                    mInputMonitor.getInputChannel(), Looper.myLooper());
-        }
+        createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         return true;
     }
 
@@ -151,25 +156,45 @@
     }
 
     @Override
-    public boolean setupWindowDecorationForTransition(
+    public void onTaskChanging(
+            RunningTaskInfo taskInfo,
+            SurfaceControl taskSurface,
+            SurfaceControl.Transaction startT,
+            SurfaceControl.Transaction finishT) {
+        final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+        if (!shouldShowWindowDecor(taskInfo)) {
+            if (decoration != null) {
+                destroyWindowDecoration(taskInfo);
+            }
+            return;
+        }
+
+        if (decoration == null) {
+            createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+        } else {
+            decoration.relayout(taskInfo, startT, finishT);
+        }
+    }
+
+    @Override
+    public void onTaskClosing(
             RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT) {
         final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
-        if (decoration == null) return false;
+        if (decoration == null) return;
 
         decoration.relayout(taskInfo, startT, finishT);
-        return true;
     }
 
     @Override
-    public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) {
+    public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
         final CaptionWindowDecoration decoration =
                 mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
-        if (decoration == null) return false;
+        if (decoration == null) return;
 
         decoration.close();
-        return true;
     }
 
     private class CaptionTouchEventListener implements
@@ -217,6 +242,7 @@
                 decoration.setButtonVisibility();
             }
         }
+
         private void injectBackKey() {
             sendBackEvent(KeyEvent.ACTION_DOWN);
             sendBackEvent(KeyEvent.ACTION_UP);
@@ -266,7 +292,7 @@
          */
         private void handleEventForMove(MotionEvent e) {
             RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-            int windowingMode =  mDesktopModeController
+            int windowingMode = mDesktopModeController
                     .getDisplayAreaWindowingMode(taskInfo.displayId);
             if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
                 return;
@@ -302,7 +328,7 @@
     }
 
     // InputEventReceiver to listen for touch input outside of caption bounds
-    private class EventReceiver extends InputEventReceiver {
+    class EventReceiver extends InputEventReceiver {
         EventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper);
         }
@@ -318,8 +344,15 @@
         }
     }
 
+    class EventReceiverFactory {
+        EventReceiver create(InputChannel channel, Looper looper) {
+            return new EventReceiver(channel, looper);
+        }
+    }
+
     /**
      * Handle MotionEvents relevant to focused task's caption that don't directly touch it
+     *
      * @param ev the {@link MotionEvent} received by {@link EventReceiver}
      */
     private void handleReceivedMotionEvent(MotionEvent ev) {
@@ -401,7 +434,6 @@
         return focusedDecor;
     }
 
-
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
         if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
         return DesktopModeStatus.IS_SUPPORTED
@@ -410,7 +442,47 @@
                 .getResources().getConfiguration().smallestScreenWidthDp >= 600;
     }
 
-    private class DragStartListenerImpl implements TaskPositioner.DragStartListener{
+    private void createWindowDecoration(
+            ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl taskSurface,
+            SurfaceControl.Transaction startT,
+            SurfaceControl.Transaction finishT) {
+        CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+        if (oldDecoration != null) {
+            // close the old decoration if it exists to avoid two window decorations being added
+            oldDecoration.close();
+        }
+        final CaptionWindowDecoration windowDecoration =
+                mCaptionWindowDecorFactory.create(
+                        mContext,
+                        mDisplayController,
+                        mTaskOrganizer,
+                        taskInfo,
+                        taskSurface,
+                        mMainHandler,
+                        mMainChoreographer,
+                        mSyncQueue);
+        mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+
+        TaskPositioner taskPositioner =
+                new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
+        CaptionTouchEventListener touchEventListener =
+                new CaptionTouchEventListener(
+                        taskInfo, taskPositioner, windowDecoration.getDragDetector());
+        windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+        windowDecoration.setDragResizeCallback(taskPositioner);
+        windowDecoration.relayout(taskInfo, startT, finishT);
+        if (mInputMonitor == null) {
+            InputManager inputManager = mInputManagerSupplier.get();
+            mInputMonitor =
+                    inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId());
+            mEventReceiver =
+                    mEventReceiverFactory.create(
+                            mInputMonitor.getInputChannel(), Looper.myLooper());
+        }
+    }
+
+    private class DragStartListenerImpl implements TaskPositioner.DragStartListener {
         @Override
         public void onDragStart(int taskId) {
             mWindowDecorByTaskId.get(taskId).closeHandleMenu();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 59576cd..037ca20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -42,7 +42,8 @@
 
 /**
  * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button.
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close
+ * button.
  *
  * The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
  */
@@ -181,12 +182,12 @@
         if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
             closeDragResizeListener();
             mDragResizeListener = new DragResizeInputListener(
-                        mContext,
-                        mHandler,
-                        mChoreographer,
-                        mDisplay.getDisplayId(),
-                        mDecorationContainerSurface,
-                        mDragResizeCallback);
+                    mContext,
+                    mHandler,
+                    mChoreographer,
+                    mDisplay.getDisplayId(),
+                    mDecorationContainerSurface,
+                    mDragResizeCallback);
         }
 
         int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
@@ -242,7 +243,6 @@
 
     /**
      * Sets the visibility of buttons and color of caption based on desktop mode status
-     *
      */
     void setButtonVisibility() {
         mDesktopActive = DesktopModeStatus.isActive(mContext);
@@ -313,6 +313,7 @@
 
     /**
      * Close an open handle menu if input is outside of menu coordinates
+     *
      * @param ev the tapped point to compare against
      */
     void closeHandleMenuIfNeeded(MotionEvent ev) {
@@ -329,6 +330,7 @@
 
     /**
      * Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+     *
      * @param ev the {@link MotionEvent} to offset
      * @return the point of the input in local space
      */
@@ -343,7 +345,8 @@
 
     /**
      * Determine if a passed MotionEvent is in a view in caption
-     * @param ev the {@link MotionEvent} to check
+     *
+     * @param ev       the {@link MotionEvent} to check
      * @param layoutId the id of the view
      * @return {@code true} if event is inside the specified view, {@code false} if not
      */
@@ -363,6 +366,7 @@
      * Check a passed MotionEvent if a click has occurred on any button on this caption
      * Note this should only be called when a regular onClick is not possible
      * (i.e. the button was clicked through status bar layer)
+     *
      * @param ev the MotionEvent to compare
      */
     void checkClickEvent(MotionEvent ev) {
@@ -399,4 +403,27 @@
         closeHandleMenu();
         super.close();
     }
+
+    static class Factory {
+
+        CaptionWindowDecoration create(
+                Context context,
+                DisplayController displayController,
+                ShellTaskOrganizer taskOrganizer,
+                ActivityManager.RunningTaskInfo taskInfo,
+                SurfaceControl taskSurface,
+                Handler handler,
+                Choreographer choreographer,
+                SyncTransactionQueue syncQueue) {
+            return new CaptionWindowDecoration(
+                    context,
+                    displayController,
+                    taskOrganizer,
+                    taskInfo,
+                    taskSurface,
+                    handler,
+                    choreographer,
+                    syncQueue);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 2ce4d04..907977c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -28,7 +28,6 @@
  * servers.
  */
 public interface WindowDecorViewModel {
-
     /**
      * Sets the transition starter that starts freeform task transitions.
      *
@@ -37,16 +36,16 @@
     void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter);
 
     /**
-     * Creates a window decoration for the given task.
-     * Can be {@code null} for Fullscreen tasks but not Freeform ones.
+     * Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but
+     * not Freeform ones.
      *
-     * @param taskInfo the initial task info of the task
+     * @param taskInfo    the initial task info of the task
      * @param taskSurface the surface of the task
-     * @param startT the start transaction to be applied before the transition
-     * @param finishT the finish transaction to restore states after the transition
+     * @param startT      the start transaction to be applied before the transition
+     * @param finishT     the finish transaction to restore states after the transition
      * @return {@code true} if window decoration was created, {@code false} otherwise
      */
-    boolean createWindowDecoration(
+    boolean onTaskOpening(
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
@@ -54,7 +53,7 @@
 
     /**
      * Notifies a task info update on the given task, with the window decoration created previously
-     * for this task by {@link #createWindowDecoration}.
+     * for this task by {@link #onTaskOpening}.
      *
      * @param taskInfo the new task info of the task
      */
@@ -62,13 +61,29 @@
 
     /**
      * Notifies a transition is about to start about the given task to give the window decoration a
-     * chance to prepare for this transition.
+     * chance to prepare for this transition. Unlike {@link #onTaskInfoChanged}, this method creates
+     * a window decoration if one does not exist but is required.
      *
-     * @param startT the start transaction to be applied before the transition
-     * @param finishT the finish transaction to restore states after the transition
-     * @return {@code true} if window decoration exists, {@code false} otherwise
+     * @param taskInfo    the initial task info of the task
+     * @param taskSurface the surface of the task
+     * @param startT      the start transaction to be applied before the transition
+     * @param finishT     the finish transaction to restore states after the transition
      */
-    boolean setupWindowDecorationForTransition(
+    void onTaskChanging(
+            ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl taskSurface,
+            SurfaceControl.Transaction startT,
+            SurfaceControl.Transaction finishT);
+
+    /**
+     * Notifies that the given task is about to close to give the window decoration a chance to
+     * prepare for this transition.
+     *
+     * @param taskInfo the initial task info of the task
+     * @param startT   the start transaction to be applied before the transition
+     * @param finishT  the finish transaction to restore states after the transition
+     */
+    void onTaskClosing(
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT);
@@ -77,7 +92,6 @@
      * Destroys the window decoration of the give task.
      *
      * @param taskInfo the info of the task
-     * @return {@code true} if window decoration was destroyed, {@code false} otherwise
      */
-    boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
-}
+    void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7ecb3f3..9215496 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -251,7 +251,9 @@
         }
 
         final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
-        final int captionWidth = loadDimensionPixelSize(resources, params.mCaptionWidthId);
+        final int captionWidth = params.mCaptionWidthId == Resources.ID_NULL
+                ? taskBounds.width()
+                : loadDimensionPixelSize(resources, params.mCaptionWidthId);
 
         startT.setPosition(
                         mCaptionContainerSurface,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 7068a84..48415d4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -103,7 +103,7 @@
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
         mTransitionObserver.onTransitionStarting(transition);
 
-        verify(mWindowDecorViewModel).createWindowDecoration(
+        verify(mWindowDecorViewModel).onTaskOpening(
                 change.getTaskInfo(), change.getLeash(), startT, finishT);
     }
 
@@ -120,7 +120,7 @@
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
         mTransitionObserver.onTransitionStarting(transition);
 
-        verify(mWindowDecorViewModel).setupWindowDecorationForTransition(
+        verify(mWindowDecorViewModel).onTaskClosing(
                 change.getTaskInfo(), startT, finishT);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
new file mode 100644
index 0000000..8b134ed
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+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.app.ActivityManager;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.GrantPermissionRule;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Tests of {@link CaptionWindowDecorViewModel} */
+@SmallTest
+public class CaptionWindowDecorViewModelTests extends ShellTestCase {
+    @Mock private CaptionWindowDecoration mCaptionWindowDecoration;
+
+    @Mock private CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+
+    @Mock private Handler mMainHandler;
+
+    @Mock private Choreographer mMainChoreographer;
+
+    @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+    @Mock private DisplayController mDisplayController;
+
+    @Mock private SyncTransactionQueue mSyncQueue;
+
+    @Mock private DesktopModeController mDesktopModeController;
+
+    @Mock private InputMonitor mInputMonitor;
+
+    @Mock private InputChannel mInputChannel;
+
+    @Mock private CaptionWindowDecorViewModel.EventReceiverFactory mEventReceiverFactory;
+
+    @Mock private CaptionWindowDecorViewModel.EventReceiver mEventReceiver;
+
+    @Mock private InputManager mInputManager;
+
+    private final List<InputManager> mMockInputManagers = new ArrayList<>();
+
+    private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel;
+
+    @Before
+    public void setUp() {
+        mMockInputManagers.add(mInputManager);
+
+        mCaptionWindowDecorViewModel =
+            new CaptionWindowDecorViewModel(
+                mContext,
+                mMainHandler,
+                mMainChoreographer,
+                mTaskOrganizer,
+                mDisplayController,
+                mSyncQueue,
+                mDesktopModeController,
+                mCaptionWindowDecorFactory,
+                new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class)));
+        mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);
+
+        doReturn(mCaptionWindowDecoration)
+            .when(mCaptionWindowDecorFactory)
+            .create(any(), any(), any(), any(), any(), any(), any(), any());
+
+        when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor);
+        when(mEventReceiverFactory.create(any(), any())).thenReturn(mEventReceiver);
+        when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel);
+    }
+
+    @Test
+    public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
+        Looper.prepare();
+        final int taskId = 1;
+        final ActivityManager.RunningTaskInfo taskInfo =
+                createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+        SurfaceControl surfaceControl = mock(SurfaceControl.class);
+        final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        GrantPermissionRule.grant(android.Manifest.permission.MONITOR_INPUT);
+
+        mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT);
+        verify(mCaptionWindowDecorFactory)
+                .create(
+                    mContext,
+                    mDisplayController,
+                    mTaskOrganizer,
+                    taskInfo,
+                    surfaceControl,
+                    mMainHandler,
+                    mMainChoreographer,
+                    mSyncQueue);
+
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+        taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+        mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+        verify(mCaptionWindowDecoration).close();
+    }
+
+    @Test
+    public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
+        final int taskId = 1;
+        final ActivityManager.RunningTaskInfo taskInfo =
+                createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED);
+        SurfaceControl surfaceControl = mock(SurfaceControl.class);
+        final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+
+        mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+        verify(mCaptionWindowDecorFactory, never())
+                .create(
+                    mContext,
+                    mDisplayController,
+                    mTaskOrganizer,
+                    taskInfo,
+                    surfaceControl,
+                    mMainHandler,
+                    mMainChoreographer,
+                    mSyncQueue);
+
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+
+        mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+        verify(mCaptionWindowDecorFactory)
+                .create(
+                    mContext,
+                    mDisplayController,
+                    mTaskOrganizer,
+                    taskInfo,
+                    surfaceControl,
+                    mMainHandler,
+                    mMainChoreographer,
+                    mSyncQueue);
+    }
+
+    private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
+        ActivityManager.RunningTaskInfo taskInfo =
+                 new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setVisible(true)
+                .build();
+        taskInfo.taskId = taskId;
+        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+        return taskInfo;
+    }
+
+    private static class MockObjectSupplier<T> implements Supplier<T> {
+        private final List<T> mObjects;
+        private final Supplier<T> mDefaultSupplier;
+        private int mNumOfCalls = 0;
+
+        private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) {
+            mObjects = objects;
+            mDefaultSupplier = defaultSupplier;
+        }
+
+        @Override
+        public T get() {
+            final T mock =
+                    mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls)
+                        : mDefaultSupplier.get();
+            ++mNumOfCalls;
+            return mock;
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 15181b1..dd9ab98 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -113,6 +113,12 @@
         mMockSurfaceControlFinishT = createMockSurfaceControlTransaction();
         mMockSurfaceControlAddWindowT = createMockSurfaceControlTransaction();
 
+        mRelayoutParams.mLayoutResId = 0;
+        mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
+        // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
+        mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+        mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
+
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
                 .create(any(), any(), any());
     }
@@ -435,6 +441,58 @@
         assertThat(additionalWindow.mWindowSurface).isNull();
     }
 
+    @Test
+    public void testLayoutResultCalculation_fullWidthCaption() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder decorContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(decorContainerSurface);
+        mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+                createMockSurfaceControlBuilder(taskBackgroundSurface);
+        mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+        mMockSurfaceControlTransactions.add(t);
+        final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+                new ActivityManager.TaskDescription.Builder()
+                        .setBackgroundColor(Color.YELLOW);
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .setTaskDescriptionBuilder(taskDescriptionBuilder)
+                .setBounds(TASK_BOUNDS)
+                .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+                .setVisible(true)
+                .build();
+        taskInfo.isFocused = true;
+        taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+        mRelayoutParams.setOutsets(
+                R.dimen.test_window_decor_left_outset,
+                R.dimen.test_window_decor_top_outset,
+                R.dimen.test_window_decor_right_outset,
+                R.dimen.test_window_decor_bottom_outset);
+        final SurfaceControl taskSurface = mock(SurfaceControl.class);
+        final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+        mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
+        windowDecor.relayout(taskInfo);
+
+        verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setContainerLayer();
+        verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
+        // Width of the captionContainerSurface should match the width of TASK_BOUNDS
+        verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
+        verify(mMockSurfaceControlStartT).show(captionContainerSurface);
+    }
+
     private TestWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
         return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
@@ -490,11 +548,6 @@
 
         @Override
         void relayout(ActivityManager.RunningTaskInfo taskInfo) {
-            mRelayoutParams.mLayoutResId = 0;
-            mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
-            mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
-            mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
-
             relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
                     mMockWindowContainerTransaction, mMockView, mRelayoutResult);
         }
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index cc7e871..68f5e4a 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1562,11 +1562,11 @@
     std::unordered_map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
 
     // Determine which ApkAssets are loaded in both theme AssetManagers.
-    const auto src_assets = source.asset_manager_->GetApkAssets();
+    const auto& src_assets = source.asset_manager_->GetApkAssets();
     for (size_t i = 0; i < src_assets.size(); i++) {
       const ApkAssets* src_asset = src_assets[i];
 
-      const auto dest_assets = asset_manager_->GetApkAssets();
+      const auto& dest_assets = asset_manager_->GetApkAssets();
       for (size_t j = 0; j < dest_assets.size(); j++) {
         const ApkAssets* dest_asset = dest_assets[j];
         if (src_asset != dest_asset) {
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index b9264c5..2d3c065 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -73,9 +73,6 @@
                                           (path != nullptr) ? base::unique_fd(-1) : std::move(fd));
 }
 
-ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
-    : value_(std::forward<std::string>(value)), is_path_(is_path) {}
-
 const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
   return is_path_ ? &value_ : nullptr;
 }
@@ -84,10 +81,14 @@
   return value_;
 }
 
+void ZipAssetsProvider::ZipCloser::operator()(ZipArchive* a) const {
+  ::CloseArchive(a);
+}
+
 ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
                                      package_property_t flags, time_t last_mod_time)
-    : zip_handle_(handle, ::CloseArchive),
-      name_(std::forward<PathOrDebugName>(path)),
+    : zip_handle_(handle),
+      name_(std::move(path)),
       flags_(flags),
       last_mod_time_(last_mod_time) {}
 
@@ -110,14 +111,12 @@
       // Stat requires execute permissions on all directories path to the file. If the process does
       // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
       // always have to return true.
-      LOG(WARNING) << "Failed to stat file '" << path << "': "
-                   << base::SystemErrorCodeToString(errno);
+      PLOG(WARNING) << "Failed to stat file '" << path << "'";
     }
   }
 
   return std::unique_ptr<ZipAssetsProvider>(
-      new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
-                                                    true /* is_path */}, flags, sb.st_mtime));
+      new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime));
 }
 
 std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
@@ -150,9 +149,8 @@
     }
   }
 
-  return std::unique_ptr<ZipAssetsProvider>(
-      new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
-                                                    false /* is_path */}, flags, sb.st_mtime));
+  return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider(
+      handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime));
 }
 
 std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -219,8 +217,9 @@
     return asset;
 }
 
-bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
-                                    const std::function<void(StringPiece, FileType)>& f) const {
+bool ZipAssetsProvider::ForEachFile(
+    const std::string& root_path,
+    base::function_ref<void(StringPiece, FileType)> f) const {
     std::string root_path_full = root_path;
     if (root_path_full.back() != '/') {
       root_path_full += '/';
@@ -297,7 +296,7 @@
 }
 
 DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
-    : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+    : dir_(std::move(path)), last_mod_time_(last_mod_time) {}
 
 std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
   struct stat sb;
@@ -312,7 +311,7 @@
     return nullptr;
   }
 
-  if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+  if (path.back() != OS_PATH_SEPARATOR) {
     path += OS_PATH_SEPARATOR;
   }
 
@@ -335,7 +334,7 @@
 
 bool DirectoryAssetsProvider::ForEachFile(
     const std::string& /* root_path */,
-    const std::function<void(StringPiece, FileType)>& /* f */) const {
+    base::function_ref<void(StringPiece, FileType)> /* f */) const {
   return true;
 }
 
@@ -362,8 +361,7 @@
 
 MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
                                          std::unique_ptr<AssetsProvider>&& secondary)
-                      : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
-                        secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+    : primary_(std::move(primary)), secondary_(std::move(secondary)) {
   debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
   path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath()
                                                           : secondary_->GetPath();
@@ -385,8 +383,9 @@
   return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
 }
 
-bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
-                                      const std::function<void(StringPiece, FileType)>& f) const {
+bool MultiAssetsProvider::ForEachFile(
+    const std::string& root_path,
+    base::function_ref<void(StringPiece, FileType)> f) const {
   return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
 }
 
@@ -424,7 +423,7 @@
 
 bool EmptyAssetsProvider::ForEachFile(
     const std::string& /* root_path */,
-    const std::function<void(StringPiece, FileType)>& /* f */) const {
+    base::function_ref<void(StringPiece, FileType)> /* f */) const {
   return true;
 }
 
@@ -447,4 +446,4 @@
   return true;
 }
 
-} // namespace android
+}  // namespace android
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index f3d2443..8983574 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -201,7 +201,7 @@
       const auto& config = configurations_[value.config_index];
       values_map[config] = value.value;
     }
-    return Result(values_map);
+    return Result(std::move(values_map));
   }
   return {};
 }
@@ -250,8 +250,7 @@
 }
 } // namespace
 
-LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
-                         const Idmap_header* header,
+LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
                          const Idmap_data_header* data_header,
                          const Idmap_target_entry* target_entries,
                          const Idmap_target_entry_inline* target_inline_entries,
@@ -259,20 +258,19 @@
                          const ConfigDescription* configs,
                          const Idmap_overlay_entry* overlay_entries,
                          std::unique_ptr<ResStringPool>&& string_pool,
-                         std::string_view overlay_apk_path,
-                         std::string_view target_apk_path)
-     : header_(header),
-       data_header_(data_header),
-       target_entries_(target_entries),
-       target_inline_entries_(target_inline_entries),
-       inline_entry_values_(inline_entry_values),
-       configurations_(configs),
-       overlay_entries_(overlay_entries),
-       string_pool_(std::move(string_pool)),
-       idmap_path_(std::move(idmap_path)),
-       overlay_apk_path_(overlay_apk_path),
-       target_apk_path_(target_apk_path),
-       idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
+                         std::string_view overlay_apk_path, std::string_view target_apk_path)
+    : header_(header),
+      data_header_(data_header),
+      target_entries_(target_entries),
+      target_inline_entries_(target_inline_entries),
+      inline_entry_values_(inline_entry_values),
+      configurations_(configs),
+      overlay_entries_(overlay_entries),
+      string_pool_(std::move(string_pool)),
+      idmap_path_(std::move(idmap_path)),
+      overlay_apk_path_(overlay_apk_path),
+      target_apk_path_(target_apk_path),
+      idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
 
 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
   ATRACE_CALL();
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index e4d1218..f10cb9b 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -105,7 +105,7 @@
   // new resource IDs.
   bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true);
 
-  inline const std::vector<const ApkAssets*> GetApkAssets() const {
+  inline const std::vector<const ApkAssets*>& GetApkAssets() const {
     return apk_assets_;
   }
 
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 7891194..d33c325 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <string>
 
+#include "android-base/function_ref.h"
 #include "android-base/macros.h"
 #include "android-base/unique_fd.h"
 
@@ -46,7 +47,7 @@
   // Iterate over all files and directories provided by the interface. The order of iteration is
   // stable.
   virtual bool ForEachFile(const std::string& path,
-                           const std::function<void(StringPiece, FileType)>& f) const = 0;
+                           base::function_ref<void(StringPiece, FileType)> f) const = 0;
 
   // Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an
   // APk, a directory, or some other file type.
@@ -90,7 +91,7 @@
                                                    off64_t len = kUnknownLength);
 
   bool ForEachFile(const std::string& root_path,
-                   const std::function<void(StringPiece, FileType)>& f) const override;
+                   base::function_ref<void(StringPiece, FileType)> f) const override;
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
@@ -108,7 +109,12 @@
                     time_t last_mod_time);
 
   struct PathOrDebugName {
-    PathOrDebugName(std::string&& value, bool is_path);
+    static PathOrDebugName Path(std::string value) {
+      return {std::move(value), true};
+    }
+    static PathOrDebugName DebugName(std::string value) {
+      return {std::move(value), false};
+    }
 
     // Retrieves the path or null if this class represents a debug name.
     WARN_UNUSED const std::string* GetPath() const;
@@ -117,11 +123,16 @@
     WARN_UNUSED const std::string& GetDebugName() const;
 
    private:
+    PathOrDebugName(std::string value, bool is_path) : value_(std::move(value)), is_path_(is_path) {
+    }
     std::string value_;
     bool is_path_;
   };
 
-  std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+  struct ZipCloser {
+    void operator()(ZipArchive* a) const;
+  };
+  std::unique_ptr<ZipArchive, ZipCloser> zip_handle_;
   PathOrDebugName name_;
   package_property_t flags_;
   time_t last_mod_time_;
@@ -132,7 +143,7 @@
   static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
 
   bool ForEachFile(const std::string& path,
-                   const std::function<void(StringPiece, FileType)>& f) const override;
+                   base::function_ref<void(StringPiece, FileType)> f) const override;
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
@@ -157,7 +168,7 @@
                                                 std::unique_ptr<AssetsProvider>&& secondary);
 
   bool ForEachFile(const std::string& root_path,
-                   const std::function<void(StringPiece, FileType)>& f) const override;
+                   base::function_ref<void(StringPiece, FileType)> f) const override;
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
@@ -184,7 +195,7 @@
   static std::unique_ptr<AssetsProvider> Create(std::string path);
 
   bool ForEachFile(const std::string& path,
-                   const std::function<void(StringPiece, FileType)>& f) const override;
+                   base::function_ref<void(StringPiece, FileType)> f) const override;
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index f173e6e..6068912 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -93,8 +93,8 @@
    public:
     Result() = default;
     explicit Result(uint32_t value) : data_(value) {};
-    explicit Result(const std::map<ConfigDescription, Res_value> &value)
-        : data_(value) { };
+    explicit Result(std::map<ConfigDescription, Res_value> value) : data_(std::move(value)) {
+    }
 
     // Returns `true` if the resource is overlaid.
     explicit operator bool() const {
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index ae7c65f..a188abb 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,7 +22,6 @@
 
 #include <cstdlib>
 #include <memory>
-#include <sstream>
 #include <vector>
 
 #include "androidfw/BigBuffer.h"
@@ -33,7 +32,14 @@
 #ifdef __ANDROID__
 #define ANDROID_LOG(x) LOG(x)
 #else
-#define ANDROID_LOG(x) std::stringstream()
+namespace android {
+// No default logging for aapt2, as it's too noisy for a command line dev tool.
+struct NullLogger {
+  template <class T>
+  friend const NullLogger& operator<<(const NullLogger& l, const T&) { return l; }
+};
+}
+#define ANDROID_LOG(x) (android::NullLogger{})
 #endif
 
 namespace android {
@@ -49,76 +55,14 @@
   return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
 }
 
-// Based on std::unique_ptr, but uses free() to release malloc'ed memory
-// without incurring the size increase of holding on to a custom deleter.
-template <typename T>
-class unique_cptr {
- public:
-  using pointer = typename std::add_pointer<T>::type;
-
-  constexpr unique_cptr() : ptr_(nullptr) {}
-  constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
-  explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
-  unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
-
-  ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
-
-  inline unique_cptr& operator=(unique_cptr&& o) noexcept {
-    if (&o == this) {
-      return *this;
-    }
-
-    std::free(reinterpret_cast<void*>(ptr_));
-    ptr_ = o.ptr_;
-    o.ptr_ = nullptr;
-    return *this;
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory.
+struct FreeDeleter {
+  void operator()(void* ptr) const {
+    ::free(ptr);
   }
-
-  inline unique_cptr& operator=(std::nullptr_t) {
-    std::free(reinterpret_cast<void*>(ptr_));
-    ptr_ = nullptr;
-    return *this;
-  }
-
-  pointer release() {
-    pointer result = ptr_;
-    ptr_ = nullptr;
-    return result;
-  }
-
-  inline pointer get() const { return ptr_; }
-
-  void reset(pointer ptr = pointer()) {
-    if (ptr == ptr_) {
-      return;
-    }
-
-    pointer old_ptr = ptr_;
-    ptr_ = ptr;
-    std::free(reinterpret_cast<void*>(old_ptr));
-  }
-
-  inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
-
-  inline explicit operator bool() const { return ptr_ != nullptr; }
-
-  inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
-
-  inline pointer operator->() const { return ptr_; }
-
-  inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
-
-  inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; }
-
-  inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
-
-  inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(unique_cptr);
-
-  pointer ptr_;
 };
+template <typename T>
+using unique_cptr = std::unique_ptr<T, FreeDeleter>;
 
 void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
 
@@ -152,13 +96,13 @@
 
 std::vector<std::string> SplitAndLowercase(android::StringPiece str, char sep);
 
-template <typename T>
-inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
-  return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+inline bool IsFourByteAligned(const void* data) {
+  return ((uintptr_t)data & 0x3U) == 0;
 }
 
-inline bool IsFourByteAligned(const void* data) {
-  return ((size_t)data & 0x3U) == 0;
+template <typename T>
+inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+  return IsFourByteAligned(data.unsafe_ptr());
 }
 
 // Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 7af5066..d3949e9 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -23,10 +23,10 @@
 
 #include "android-base/logging.h"
 
-#ifndef _WIN32
+#ifdef __linux__
 #include <sys/statvfs.h>
 #include <sys/vfs.h>
-#endif  // _WIN32
+#endif  // __linux__
 
 #include <cstring>
 #include <cstdio>
@@ -86,15 +86,15 @@
     return sb.st_mtime;
 }
 
-#ifdef _WIN32
-// No need to implement these for Windows, the functions only matter on a device.
+#ifndef __linux__
+// No need to implement these on the host, the functions only matter on a device.
 bool isReadonlyFilesystem(const char*) {
     return false;
 }
 bool isReadonlyFilesystem(int) {
     return false;
 }
-#else   // _WIN32
+#else   // __linux__
 bool isReadonlyFilesystem(const char* path) {
     struct statfs sfs;
     if (::statfs(path, &sfs)) {
@@ -112,6 +112,6 @@
     }
     return (sfs.f_flags & ST_RDONLY) != 0;
 }
-#endif  // _WIN32
+#endif  // __linux__
 
 }; // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index c0a4fdf..fcc0126 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -570,6 +570,7 @@
                 "renderthread/VulkanSurface.cpp",
                 "renderthread/RenderProxy.cpp",
                 "renderthread/RenderThread.cpp",
+                "renderthread/HintSessionWrapper.cpp",
                 "service/GraphicsStatsService.cpp",
                 "thread/CommonPool.cpp",
                 "utils/GLUtils.cpp",
@@ -589,6 +590,7 @@
                 "ProfileData.cpp",
                 "ProfileDataContainer.cpp",
                 "Readback.cpp",
+                "Tonemapper.cpp",
                 "TreeInfo.cpp",
                 "WebViewFunctorManager.cpp",
                 "protos/graphicsstats.proto",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 673041a..cd4fae8 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -17,6 +17,7 @@
 #include "CanvasTransform.h"
 
 #include <SkAndroidFrameworkUtils.h>
+#include <SkBlendMode.h>
 #include <SkColorFilter.h>
 #include <SkGradientShader.h>
 #include <SkHighContrastFilter.h>
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9a4c550..a7f8f61 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <SkBlendMode.h>
 #include <SkColorFilter.h>
 #include <SkImage.h>
 #include <SkMatrix.h>
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 564ee4f..b15b6cb 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -104,6 +104,7 @@
         set(FrameInfoIndex::AnimationStart) = vsyncTime;
         set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
         set(FrameInfoIndex::DrawStart) = vsyncTime;
+        set(FrameInfoIndex::FrameStartTime) = vsyncTime;
         set(FrameInfoIndex::FrameDeadline) = frameDeadline;
         set(FrameInfoIndex::FrameInterval) = frameInterval;
         return *this;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 9053c12..fc3118a 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
 #include "utils/Color.h"
 #include "utils/MathUtils.h"
 
+#include <SkBlendMode.h>
+
 #include <log/log.h>
 
 namespace android {
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 02c2e67..8dcd6db 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -16,16 +16,6 @@
 
 #include "Readback.h"
 
-#include <sync/sync.h>
-#include <system/window.h>
-
-#include <gui/TraceUtils.h>
-#include "DeferredLayerUpdater.h"
-#include "Properties.h"
-#include "hwui/Bitmap.h"
-#include "pipeline/skia/LayerDrawable.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/VulkanManager.h"
 #include <SkBitmap.h>
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
@@ -38,6 +28,19 @@
 #include <SkRefCnt.h>
 #include <SkSamplingOptions.h>
 #include <SkSurface.h>
+#include <gui/TraceUtils.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <shaders/shaders.h>
+#include <sync/sync.h>
+#include <system/window.h>
+
+#include "DeferredLayerUpdater.h"
+#include "Properties.h"
+#include "Tonemapper.h"
+#include "hwui/Bitmap.h"
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
 #include "utils/Color.h"
 #include "utils/MathUtils.h"
 #include "utils/NdkUtils.h"
@@ -91,8 +94,18 @@
         }
     }
 
-    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
-            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
+    int32_t dataspace = ANativeWindow_getBuffersDataSpace(window);
+
+    // If the application is not updating the Surface themselves, e.g., another
+    // process is producing buffers for the application to display, then
+    // ANativeWindow_getBuffersDataSpace will return an unknown answer, so grab
+    // the dataspace from buffer metadata instead, if it exists.
+    if (dataspace == 0) {
+        dataspace = AHardwareBuffer_getDataSpace(sourceBuffer.get());
+    }
+
+    sk_sp<SkColorSpace> colorSpace =
+            DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace));
     sk_sp<SkImage> image =
             SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
 
@@ -227,6 +240,10 @@
     const bool hasBufferCrop = cropRect.left < cropRect.right && cropRect.top < cropRect.bottom;
     auto constraint =
             hasBufferCrop ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint;
+
+    static constexpr float kMaxLuminanceNits = 4000.f;
+    tonemapPaint(image->imageInfo(), canvas->imageInfo(), kMaxLuminanceNits, paint);
+
     canvas->drawImageRect(image, imageSrcRect, imageDstRect, sampling, &paint, constraint);
     canvas->restore();
 
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f5ebfd5..f070e97 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -22,6 +22,7 @@
 #include <experimental/type_traits>
 
 #include "SkAndroidFrameworkUtils.h"
+#include "SkBlendMode.h"
 #include "SkCanvas.h"
 #include "SkCanvasPriv.h"
 #include "SkColor.h"
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 35bec93..f37729e 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -34,6 +34,7 @@
 #include <SkRuntimeEffect.h>
 #include <vector>
 
+enum class SkBlendMode;
 class SkRRect;
 
 namespace android {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 51007c5..eece77e 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -33,6 +33,7 @@
 #include <cassert>
 #include <optional>
 
+enum class SkBlendMode;
 class SkRRect;
 
 namespace android {
diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp
new file mode 100644
index 0000000..a7e76b6
--- /dev/null
+++ b/libs/hwui/Tonemapper.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Tonemapper.h"
+
+#include <SkRuntimeEffect.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
+
+#include "utils/Color.h"
+
+namespace android::uirenderer {
+
+namespace {
+
+class ColorFilterRuntimeEffectBuilder : public SkRuntimeEffectBuilder {
+public:
+    explicit ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
+            : SkRuntimeEffectBuilder(std::move(effect)) {}
+
+    sk_sp<SkColorFilter> makeColorFilter() {
+        return this->effect()->makeColorFilter(this->uniforms());
+    }
+};
+
+static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearEffect& linearEffect,
+                                                          float maxDisplayLuminance,
+                                                          float currentDisplayLuminanceNits,
+                                                          float maxLuminance) {
+    auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
+    auto [runtimeEffect, error] = SkRuntimeEffect::MakeForColorFilter(std::move(shaderString));
+    if (!runtimeEffect) {
+        LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+    }
+
+    ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect));
+
+    const auto uniforms =
+            shaders::buildLinearEffectUniforms(linearEffect, android::mat4(), maxDisplayLuminance,
+                                               currentDisplayLuminanceNits, maxLuminance);
+
+    for (const auto& uniform : uniforms) {
+        effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
+    }
+
+    return effectBuilder.makeColorFilter();
+}
+
+static bool extractTransfer(ui::Dataspace dataspace) {
+    return dataspace & HAL_DATASPACE_TRANSFER_MASK;
+}
+
+static bool isHdrDataspace(ui::Dataspace dataspace) {
+    const auto transfer = extractTransfer(dataspace);
+
+    return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+static ui::Dataspace getDataspace(const SkImageInfo& image) {
+    return static_cast<ui::Dataspace>(
+            ColorSpaceToADataSpace(image.colorSpace(), image.colorType()));
+}
+
+}  // namespace
+
+// Given a source and destination image info, and the max content luminance, generate a tonemaping
+// shader and tag it on the supplied paint.
+void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
+                  SkPaint& paint) {
+    const auto sourceDataspace = getDataspace(source);
+    const auto destinationDataspace = getDataspace(destination);
+
+    if (extractTransfer(sourceDataspace) != extractTransfer(destinationDataspace) &&
+        (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace))) {
+        const auto effect = shaders::LinearEffect{
+                .inputDataspace = sourceDataspace,
+                .outputDataspace = destinationDataspace,
+                .undoPremultipliedAlpha = source.alphaType() == kPremul_SkAlphaType,
+                .fakeInputDataspace = destinationDataspace,
+                .type = shaders::LinearEffect::SkSLType::ColorFilter};
+        constexpr float kMaxDisplayBrightnessNits = 1000.f;
+        constexpr float kCurrentDisplayBrightnessNits = 500.f;
+        sk_sp<SkColorFilter> colorFilter = createLinearEffectColorFilter(
+                effect, kMaxDisplayBrightnessNits, kCurrentDisplayBrightnessNits, maxLuminanceNits);
+
+        if (paint.getColorFilter()) {
+            paint.setColorFilter(SkColorFilters::Compose(paint.refColorFilter(), colorFilter));
+        } else {
+            paint.setColorFilter(colorFilter);
+        }
+    }
+}
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/Tonemapper.h b/libs/hwui/Tonemapper.h
new file mode 100644
index 0000000..c0d5325
--- /dev/null
+++ b/libs/hwui/Tonemapper.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+
+namespace android::uirenderer {
+
+// Given a source and destination image info, and the max content luminance, generate a tonemaping
+// shader and tag it on the supplied paint.
+void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
+                  SkPaint& paint);
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp
index 70bd085..cc79cba 100644
--- a/libs/hwui/apex/android_paint.cpp
+++ b/libs/hwui/apex/android_paint.cpp
@@ -19,6 +19,7 @@
 #include "TypeCast.h"
 
 #include <hwui/Paint.h>
+#include <SkBlendMode.h>
 
 using namespace android;
 
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index d4b0198..8b52551 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "BlurDrawLooper.h"
+#include <SkBlurTypes.h>
 #include <SkColorSpace.h>
 #include <SkMaskFilter.h>
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 82d23b5..4608088 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -30,6 +30,7 @@
 #include <SkMatrix.h>
 
 class SkAnimatedImage;
+enum class SkBlendMode;
 class SkCanvasState;
 class SkRRect;
 class SkRuntimeShaderBuilder;
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index cef21f9..4bd7ef4 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -17,6 +17,7 @@
 
 #include "GraphicsJNI.h"
 
+#include "SkBlendMode.h"
 #include "SkColorFilter.h"
 #include "SkColorMatrixFilter.h"
 
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 5383032..048ce02 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -2,6 +2,7 @@
 #include "SkMaskFilter.h"
 #include "SkBlurMask.h"
 #include "SkBlurMaskFilter.h"
+#include "SkBlurTypes.h"
 #include "SkTableMaskFilter.h"
 
 static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 213f35a..f3db170 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -15,6 +15,7 @@
  */
 #include "Bitmap.h"
 #include "GraphicsJNI.h"
+#include "SkBlendMode.h"
 #include "SkImageFilter.h"
 #include "SkImageFilters.h"
 #include "graphics_jni_helpers.h"
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 3ba5409..99f54c1 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -25,6 +25,7 @@
 #include "SkColorFilter.h"
 #include "SkRuntimeEffect.h"
 #include "SkSurface.h"
+#include "Tonemapper.h"
 #include "gl/GrGLTypes.h"
 #include "math/mat4.h"
 #include "system/graphics-base-v1.0.h"
@@ -76,37 +77,6 @@
              isIntegerAligned(dstDevRect.y()));
 }
 
-static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
-                                                const shaders::LinearEffect& linearEffect,
-                                                float maxDisplayLuminance,
-                                                float currentDisplayLuminanceNits,
-                                                float maxLuminance) {
-    auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
-    auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
-    if (!runtimeEffect) {
-        LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
-    }
-
-    SkRuntimeShaderBuilder effectBuilder(std::move(runtimeEffect));
-
-    effectBuilder.child("child") = std::move(shader);
-
-    const auto uniforms = shaders::buildLinearEffectUniforms(
-            linearEffect, mat4(), maxDisplayLuminance, currentDisplayLuminanceNits, maxLuminance);
-
-    for (const auto& uniform : uniforms) {
-        effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
-    }
-
-    return effectBuilder.makeShader();
-}
-
-static bool isHdrDataspace(ui::Dataspace dataspace) {
-    const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
-
-    return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
-}
-
 static void adjustCropForYUV(uint32_t format, int bufferWidth, int bufferHeight, SkRect* cropRect) {
     // Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by
     // a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an
@@ -215,31 +185,10 @@
             sampling = SkSamplingOptions(SkFilterMode::kLinear);
         }
 
-        const auto sourceDataspace = static_cast<ui::Dataspace>(
-                ColorSpaceToADataSpace(layerImage->colorSpace(), layerImage->colorType()));
-        const SkImageInfo& imageInfo = canvas->imageInfo();
-        const auto destinationDataspace = static_cast<ui::Dataspace>(
-                ColorSpaceToADataSpace(imageInfo.colorSpace(), imageInfo.colorType()));
-
-        if (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace)) {
-            const auto effect = shaders::LinearEffect{
-                    .inputDataspace = sourceDataspace,
-                    .outputDataspace = destinationDataspace,
-                    .undoPremultipliedAlpha = layerImage->alphaType() == kPremul_SkAlphaType,
-                    .fakeInputDataspace = destinationDataspace};
-            auto shader = layerImage->makeShader(sampling,
-                                                 SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
-            constexpr float kMaxDisplayBrightess = 1000.f;
-            constexpr float kCurrentDisplayBrightness = 500.f;
-            shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
-                                              kCurrentDisplayBrightness,
-                                              layer->getMaxLuminanceNits());
-            paint.setShader(shader);
-            canvas->drawRect(skiaDestRect, paint);
-        } else {
-            canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
-                                  constraint);
-        }
+        tonemapPaint(layerImage->imageInfo(), canvas->imageInfo(), layer->getMaxLuminanceNits(),
+                     paint);
+        canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+                              constraint);
 
         canvas->restore();
         // restore the original matrix
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index 6c390c3..c7582e7 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -18,6 +18,7 @@
 
 #include "SkiaUtils.h"
 
+#include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkDrawable.h>
 #include <SkMatrix.h>
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index 2dbeb3a..b169c92 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 #include "StretchMask.h"
-#include "SkSurface.h"
+
+#include "SkBlendMode.h"
 #include "SkCanvas.h"
+#include "SkSurface.h"
 #include "TransformCanvas.h"
 #include "SkiaDisplayList.h"
 
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 3c7617d..e168a7b 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -33,6 +33,8 @@
 #include "thread/ThreadBase.h"
 #include "utils/TimeUtils.h"
 
+#include <SkBlendMode.h>
+
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 1d24e71..1c76884 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -98,7 +98,6 @@
     auto& cache = skiapipeline::ShaderCache::get();
     cache.initShaderDiskCache(identity, size);
     contextOptions->fPersistentCache = &cache;
-    contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
 }
 
 void CacheManager::trimMemory(TrimLevel mode) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d09bc47..64839d0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -71,16 +71,19 @@
 } /* namespace */
 
 CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
-                                     RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+                                     RenderNode* rootRenderNode, IContextFactory* contextFactory,
+                                     int32_t uiThreadId, int32_t renderThreadId) {
     auto renderType = Properties::getRenderPipelineType();
 
     switch (renderType) {
         case RenderPipelineType::SkiaGL:
             return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
-                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread),
+                                     uiThreadId, renderThreadId);
         case RenderPipelineType::SkiaVulkan:
             return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
-                                     std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+                                     std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
+                                     uiThreadId, renderThreadId);
         default:
             LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
             break;
@@ -110,7 +113,8 @@
 
 CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                              IContextFactory* contextFactory,
-                             std::unique_ptr<IRenderPipeline> renderPipeline)
+                             std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId,
+                             pid_t renderThreadId)
         : mRenderThread(thread)
         , mGenerationID(0)
         , mOpaque(!translucent)
@@ -118,7 +122,8 @@
         , mJankTracker(&thread.globalProfileData())
         , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
         , mContentDrawBounds(0, 0, 0, 0)
-        , mRenderPipeline(std::move(renderPipeline)) {
+        , mRenderPipeline(std::move(renderPipeline))
+        , mHintSessionWrapper(uiThreadId, renderThreadId) {
     mRenderThread.cacheManager().registerCanvasContext(this);
     rootRenderNode->makeRoot();
     mRenderNodes.emplace_back(rootRenderNode);
@@ -472,16 +477,22 @@
     mRenderThread.pushBackFrameCallback(this);
 }
 
-std::optional<nsecs_t> CanvasContext::draw() {
+void CanvasContext::draw() {
     if (auto grContext = getGrContext()) {
         if (grContext->abandoned()) {
             LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
-            return std::nullopt;
+            return;
         }
     }
     SkRect dirty;
     mDamageAccumulator.finish(&dirty);
 
+    // reset syncDelayDuration each time we draw
+    nsecs_t syncDelayDuration = mSyncDelayDuration;
+    nsecs_t idleDuration = mIdleDuration;
+    mSyncDelayDuration = 0;
+    mIdleDuration = 0;
+
     if (!Properties::isDrawingEnabled() ||
         (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -498,7 +509,7 @@
             std::invoke(func, false /* didProduceBuffer */);
         }
         mFrameCommitCallbacks.clear();
-        return std::nullopt;
+        return;
     }
 
     ScopedActiveContext activeContext(this);
@@ -650,10 +661,25 @@
         }
     }
 
+    int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
+    int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
+    int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+
+    mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+
+    if (didDraw) {
+        int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+        int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+        int64_t actualDuration = frameDuration -
+                                 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+                                 dequeueBufferDuration - idleDuration;
+        mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+    }
+
+    mLastDequeueBufferDuration = dequeueBufferDuration;
+
     mRenderThread.cacheManager().onFrameCompleted();
-    return didDraw ? std::make_optional(
-                             mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
-                   : std::nullopt;
+    return;
 }
 
 void CanvasContext::reportMetricsWithPresentTime() {
@@ -766,6 +792,8 @@
 // Called by choreographer to do an RT-driven animation
 void CanvasContext::doFrame() {
     if (!mRenderPipeline->isSurfaceReady()) return;
+    mIdleDuration =
+            systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos();
     prepareAndDraw(nullptr);
 }
 
@@ -974,6 +1002,14 @@
     }
 }
 
+void CanvasContext::sendLoadResetHint() {
+    mHintSessionWrapper.sendLoadResetHint();
+}
+
+void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
+    mSyncDelayDuration = duration;
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index db96cfb..e875c42 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,22 +16,6 @@
 
 #pragma once
 
-#include "DamageAccumulator.h"
-#include "FrameInfo.h"
-#include "FrameInfoVisualizer.h"
-#include "FrameMetricsReporter.h"
-#include "IContextFactory.h"
-#include "IRenderPipeline.h"
-#include "JankTracker.h"
-#include "LayerUpdateQueue.h"
-#include "Lighting.h"
-#include "ReliableSurface.h"
-#include "RenderNode.h"
-#include "renderthread/RenderTask.h"
-#include "renderthread/RenderThread.h"
-#include "utils/RingBuffer.h"
-#include "ColorMode.h"
-
 #include <SkBitmap.h>
 #include <SkRect.h>
 #include <SkSize.h>
@@ -46,6 +30,23 @@
 #include <utility>
 #include <vector>
 
+#include "ColorMode.h"
+#include "DamageAccumulator.h"
+#include "FrameInfo.h"
+#include "FrameInfoVisualizer.h"
+#include "FrameMetricsReporter.h"
+#include "HintSessionWrapper.h"
+#include "IContextFactory.h"
+#include "IRenderPipeline.h"
+#include "JankTracker.h"
+#include "LayerUpdateQueue.h"
+#include "Lighting.h"
+#include "ReliableSurface.h"
+#include "RenderNode.h"
+#include "renderthread/RenderTask.h"
+#include "renderthread/RenderThread.h"
+#include "utils/RingBuffer.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -66,7 +67,8 @@
 class CanvasContext : public IFrameCallback {
 public:
     static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
-                                 IContextFactory* contextFactory);
+                                 IContextFactory* contextFactory, pid_t uiThreadId,
+                                 pid_t renderThreadId);
     virtual ~CanvasContext();
 
     /**
@@ -138,7 +140,7 @@
     bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
     // Returns the DequeueBufferDuration.
-    std::optional<nsecs_t> draw();
+    void draw();
     void destroy();
 
     // IFrameCallback, Choreographer-driven frame callback entry point
@@ -214,9 +216,14 @@
 
     static CanvasContext* getActiveContext();
 
+    void sendLoadResetHint();
+
+    void setSyncDelayDuration(nsecs_t duration);
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
-                  IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+                  IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
+                  pid_t uiThreadId, pid_t renderThreadId);
 
     friend class RegisterFrameCallbackTask;
     // TODO: Replace with something better for layer & other GL object
@@ -330,6 +337,11 @@
 
     std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
     std::function<void()> mPrepareSurfaceControlForWebviewCallback;
+
+    HintSessionWrapper mHintSessionWrapper;
+    nsecs_t mLastDequeueBufferDuration = 0;
+    nsecs_t mSyncDelayDuration = 0;
+    nsecs_t mIdleDuration = 0;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb30614..1cc82fd 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,7 +16,6 @@
 
 #include "DrawFrameTask.h"
 
-#include <dlfcn.h>
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
 
@@ -28,70 +27,11 @@
 #include "../RenderNode.h"
 #include "CanvasContext.h"
 #include "RenderThread.h"
-#include "thread/CommonPool.h"
-#include "utils/TimeUtils.h"
 
 namespace android {
 namespace uirenderer {
 namespace renderthread {
 
-namespace {
-
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
-                                                      size_t, int64_t);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_sendHint gAPH_sendHintFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
-    if (gAPerformanceHintBindingInitialized) return;
-
-    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
-    LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
-
-    gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
-    LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
-                        "Failed to find required symbol APerformanceHint_getManager!");
-
-    gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
-    LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
-                        "Failed to find required symbol APerformanceHint_createSession!");
-
-    gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
-            handle_, "APerformanceHint_updateTargetWorkDuration");
-    LOG_ALWAYS_FATAL_IF(
-            gAPH_updateTargetWorkDurationFn == nullptr,
-            "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
-
-    gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
-            handle_, "APerformanceHint_reportActualWorkDuration");
-    LOG_ALWAYS_FATAL_IF(
-            gAPH_reportActualWorkDurationFn == nullptr,
-            "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
-    gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
-    LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
-                        "Failed to find required symbol APerformanceHint_sendHint!");
-
-    gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
-    LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
-                        "Failed to find required symbol APerformanceHint_closeSession!");
-
-    gAPerformanceHintBindingInitialized = true;
-}
-
-}  // namespace
-
 DrawFrameTask::DrawFrameTask()
         : mRenderThread(nullptr)
         , mContext(nullptr)
@@ -100,13 +40,11 @@
 
 DrawFrameTask::~DrawFrameTask() {}
 
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
-                               int32_t uiThreadId, int32_t renderThreadId) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+                               RenderNode* targetNode) {
     mRenderThread = thread;
     mContext = context;
     mTargetNode = targetNode;
-    mUiThreadId = uiThreadId;
-    mRenderThreadId = renderThreadId;
 }
 
 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -150,11 +88,11 @@
 void DrawFrameTask::run() {
     const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
     ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
-    nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
+
+    mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
 
     bool canUnblockUiThread;
     bool canDrawThisFrame;
-    bool didDraw = false;
     {
         TreeInfo info(TreeInfo::MODE_FULL, *mContext);
         info.forceDrawFrame = mForceDrawFrame;
@@ -175,9 +113,6 @@
     std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
     mFrameCallback = nullptr;
     mFrameCompleteCallback = nullptr;
-    int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
-    int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
-    int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
 
     // From this point on anything in "this" is *UNSAFE TO ACCESS*
     if (canUnblockUiThread) {
@@ -188,18 +123,15 @@
     if (CC_UNLIKELY(frameCallback)) {
         context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
                                    frameNr = context->getFrameNumber()]() {
-            auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+            auto frameCommitCallback = frameCallback(syncResult, frameNr);
             if (frameCommitCallback) {
                 context->addFrameCommitListener(std::move(frameCommitCallback));
             }
         });
     }
 
-    nsecs_t dequeueBufferDuration = 0;
     if (CC_LIKELY(canDrawThisFrame)) {
-        std::optional<nsecs_t> drawResult = context->draw();
-        didDraw = drawResult.has_value();
-        dequeueBufferDuration = drawResult.value_or(0);
+        context->draw();
     } else {
         // Do a flush in case syncFrameState performed any texture uploads. Since we skipped
         // the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -218,41 +150,6 @@
     if (!canUnblockUiThread) {
         unblockUiThread();
     }
-
-    if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-
-    constexpr int64_t kSanityCheckLowerBound = 100_us;
-    constexpr int64_t kSanityCheckUpperBound = 10_s;
-    int64_t targetWorkDuration = frameDeadline - intendedVsync;
-    targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
-    if (targetWorkDuration > kSanityCheckLowerBound &&
-        targetWorkDuration < kSanityCheckUpperBound &&
-        targetWorkDuration != mLastTargetWorkDuration) {
-        mLastTargetWorkDuration = targetWorkDuration;
-        mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
-    }
-
-    if (didDraw) {
-        int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
-        int64_t actualDuration = frameDuration -
-                                 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
-                                 dequeueBufferDuration;
-        if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
-            mHintSessionWrapper->reportActualWorkDuration(actualDuration);
-        }
-    }
-
-    mLastDequeueBufferDuration = dequeueBufferDuration;
-}
-
-void DrawFrameTask::sendLoadResetHint() {
-    if (!(Properties::useHintManager && Properties::isDrawingEnabled())) return;
-    if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-    nsecs_t now = systemTime();
-    if (now - mLastFrameNotification > kResetHintTimeout) {
-        mHintSessionWrapper->sendHint(SessionHint::CPU_LOAD_RESET);
-    }
-    mLastFrameNotification = now;
 }
 
 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
@@ -305,50 +202,6 @@
     mSignal.signal();
 }
 
-DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
-    if (!Properties::useHintManager) return;
-    if (uiThreadId < 0 || renderThreadId < 0) return;
-
-    ensureAPerformanceHintBindingInitialized();
-
-    APerformanceHintManager* manager = gAPH_getManagerFn();
-    if (!manager) return;
-
-    std::vector<int32_t> tids = CommonPool::getThreadIds();
-    tids.push_back(uiThreadId);
-    tids.push_back(renderThreadId);
-
-    // DrawFrameTask code will always set a target duration before reporting actual durations.
-    // So this is just a placeholder value that's never used.
-    int64_t dummyTargetDurationNanos = 16666667;
-    mHintSession =
-            gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
-}
-
-DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
-    if (mHintSession) {
-        gAPH_closeSessionFn(mHintSession);
-    }
-}
-
-void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
-    if (mHintSession) {
-        gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
-    }
-}
-
-void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
-    if (mHintSession) {
-        gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
-    }
-}
-
-void DrawFrameTask::HintSessionWrapper::sendHint(SessionHint hint) {
-    if (mHintSession && Properties::isDrawingEnabled()) {
-        gAPH_sendHintFn(mHintSession, static_cast<int>(hint));
-    }
-}
-
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 7eae41c..fafab24 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,7 +16,6 @@
 #ifndef DRAWFRAMETASK_H
 #define DRAWFRAMETASK_H
 
-#include <android/performance_hint.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <utils/StrongPointer.h>
@@ -28,7 +27,6 @@
 #include "../Rect.h"
 #include "../TreeInfo.h"
 #include "RenderTask.h"
-#include "utils/TimeUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -62,8 +60,7 @@
     DrawFrameTask();
     virtual ~DrawFrameTask();
 
-    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
-                    int32_t uiThreadId, int32_t renderThreadId);
+    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
     void setContentDrawBounds(int left, int top, int right, int bottom) {
         mContentDrawBounds.set(left, top, right, bottom);
     }
@@ -91,22 +88,7 @@
 
     void forceDrawNextFrame() { mForceDrawFrame = true; }
 
-    void sendLoadResetHint();
-
 private:
-    class HintSessionWrapper {
-    public:
-        HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
-        ~HintSessionWrapper();
-
-        void updateTargetWorkDuration(long targetDurationNanos);
-        void reportActualWorkDuration(long actualDurationNanos);
-        void sendHint(SessionHint hint);
-
-    private:
-        APerformanceHintSession* mHintSession = nullptr;
-    };
-
     void postAndWait();
     bool syncFrameState(TreeInfo& info);
     void unblockUiThread();
@@ -117,8 +99,6 @@
     RenderThread* mRenderThread;
     CanvasContext* mContext;
     RenderNode* mTargetNode = nullptr;
-    int32_t mUiThreadId = -1;
-    int32_t mRenderThreadId = -1;
     Rect mContentDrawBounds;
 
     /*********************************************
@@ -135,13 +115,6 @@
     std::function<void(bool)> mFrameCommitCallback;
     std::function<void()> mFrameCompleteCallback;
 
-    nsecs_t mLastDequeueBufferDuration = 0;
-    nsecs_t mLastTargetWorkDuration = 0;
-    std::optional<HintSessionWrapper> mHintSessionWrapper;
-
-    nsecs_t mLastFrameNotification = 0;
-    nsecs_t kResetHintTimeout = 100_ms;
-
     bool mForceDrawFrame = false;
 };
 
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 0000000..edacef0
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HintSessionWrapper.h"
+
+#include <dlfcn.h>
+#include <utils/Log.h>
+
+#include <vector>
+
+#include "../Properties.h"
+#include "thread/CommonPool.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+                                                      size_t, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_sendHint gAPH_sendHintFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+    if (gAPerformanceHintBindingInitialized) return;
+
+    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+    LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+    gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+    LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+                        "Failed to find required symbol APerformanceHint_getManager!");
+
+    gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+    LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+                        "Failed to find required symbol APerformanceHint_createSession!");
+
+    gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+    LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+                        "Failed to find required symbol APerformanceHint_closeSession!");
+
+    gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
+            handle_, "APerformanceHint_updateTargetWorkDuration");
+    LOG_ALWAYS_FATAL_IF(
+            gAPH_updateTargetWorkDurationFn == nullptr,
+            "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+
+    gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
+            handle_, "APerformanceHint_reportActualWorkDuration");
+    LOG_ALWAYS_FATAL_IF(
+            gAPH_reportActualWorkDurationFn == nullptr,
+            "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
+
+    gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+    LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+                        "Failed to find required symbol APerformanceHint_sendHint!");
+
+    gAPerformanceHintBindingInitialized = true;
+}
+
+}  // namespace
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+        : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+
+HintSessionWrapper::~HintSessionWrapper() {
+    if (mHintSession) {
+        gAPH_closeSessionFn(mHintSession);
+    }
+}
+
+bool HintSessionWrapper::useHintSession() {
+    if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
+    if (mHintSession) return true;
+    // If session does not exist, create it;
+    // this defers session creation until we try to actually use it.
+    if (!mSessionValid) return false;
+    return init();
+}
+
+bool HintSessionWrapper::init() {
+    if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+
+    // Assume that if we return before the end, it broke
+    mSessionValid = false;
+
+    ensureAPerformanceHintBindingInitialized();
+
+    APerformanceHintManager* manager = gAPH_getManagerFn();
+    if (!manager) return false;
+
+    std::vector<pid_t> tids = CommonPool::getThreadIds();
+    tids.push_back(mUiThreadId);
+    tids.push_back(mRenderThreadId);
+
+    // Use a placeholder target value to initialize,
+    // this will always be replaced elsewhere before it gets used
+    int64_t defaultTargetDurationNanos = 16666667;
+    mHintSession =
+            gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+
+    mSessionValid = !!mHintSession;
+    return mSessionValid;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
+    if (!useHintSession()) return;
+    targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
+    if (targetWorkDurationNanos != mLastTargetWorkDuration &&
+        targetWorkDurationNanos > kSanityCheckLowerBound &&
+        targetWorkDurationNanos < kSanityCheckUpperBound) {
+        mLastTargetWorkDuration = targetWorkDurationNanos;
+        gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+    }
+    mLastFrameNotification = systemTime();
+}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+    if (!useHintSession()) return;
+    if (actualDurationNanos > kSanityCheckLowerBound &&
+        actualDurationNanos < kSanityCheckUpperBound) {
+        gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+    }
+}
+
+void HintSessionWrapper::sendLoadResetHint() {
+    if (!useHintSession()) return;
+    nsecs_t now = systemTime();
+    if (now - mLastFrameNotification > kResetHintTimeout) {
+        gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+    }
+    mLastFrameNotification = now;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
new file mode 100644
index 0000000..fcbc101
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/performance_hint.h>
+
+#include "utils/TimeUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+
+class HintSessionWrapper {
+public:
+    HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
+    ~HintSessionWrapper();
+
+    void updateTargetWorkDuration(long targetDurationNanos);
+    void reportActualWorkDuration(long actualDurationNanos);
+    void sendLoadResetHint();
+
+private:
+    bool useHintSession();
+    bool init();
+    APerformanceHintSession* mHintSession = nullptr;
+
+    nsecs_t mLastFrameNotification = 0;
+    nsecs_t mLastTargetWorkDuration = 0;
+
+    pid_t mUiThreadId;
+    pid_t mRenderThreadId;
+
+    bool mSessionValid = true;
+
+    static constexpr nsecs_t kResetHintTimeout = 100_ms;
+    static constexpr int64_t kSanityCheckLowerBound = 100_us;
+    static constexpr int64_t kSanityCheckUpperBound = 10_s;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 03a2bc9..07f5a78 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,11 +42,13 @@
 RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                          IContextFactory* contextFactory)
         : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
-    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
-        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+    pid_t uiThreadId = pthread_gettid_np(pthread_self());
+    pid_t renderThreadId = getRenderThreadTid();
+    mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory,
+                                     uiThreadId, renderThreadId);
     });
-    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
-                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
+    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
 }
 
 RenderProxy::~RenderProxy() {
@@ -55,7 +57,7 @@
 
 void RenderProxy::destroyContext() {
     if (mContext) {
-        mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
+        mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
         // This is also a fence as we need to be certain that there are no
         // outstanding mDrawFrame tasks posted before it is destroyed
         mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -237,7 +239,7 @@
 }
 
 void RenderProxy::notifyCallbackPending() {
-    mDrawFrameTask.sendLoadResetHint();
+    mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
 }
 
 void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index d3c41191..dc36a2e 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -19,6 +19,8 @@
 #include <SkCanvasVirtualEnforcer.h>
 #include <SkNoDrawCanvas.h>
 
+enum class SkBlendMode;
+
 namespace android {
 namespace uirenderer {
 namespace test {
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
index 43df4a0..e70d44c 100644
--- a/libs/hwui/tests/common/TestListViewSceneBase.cpp
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -19,6 +19,8 @@
 #include "TestContext.h"
 #include "TestUtils.h"
 
+#include <SkBlendMode.h>
+
 #include <utils/Color.h>
 
 namespace android {
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 5af7d43..19e87f8 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -19,6 +19,7 @@
 #include "utils/Color.h"
 
 #include <SkBitmap.h>
+#include <SkBlendMode.h>
 
 using namespace android;
 using namespace android::uirenderer;
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 2a016ac..3a1ea8c 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class ClippingAnimation;
 
 static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 4271d2f..484289a 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -20,6 +20,8 @@
 #include <hwui/Paint.h>
 #include <minikin/Layout.h>
 
+#include <SkBlendMode.h>
+
 #include <cstdio>
 
 class GlyphStressAnimation;
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 0d5ca6d..dfdd0d8 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -17,6 +17,7 @@
 #include "TestSceneBase.h"
 #include "utils/Color.h"
 
+#include <SkBlendMode.h>
 #include <SkColorSpace.h>
 #include <SkGradientShader.h>
 #include <SkImagePriv.h>
diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
index cac2fb3..2955fb2 100644
--- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class HwLayerAnimation;
 
 static TestScene::Registrar _HwLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
index 77a59df..8c9a614 100644
--- a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class HwLayerSizeAnimation;
 
 static TestScene::Registrar _HwLayerSize(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/JankyScene.cpp b/libs/hwui/tests/common/scenes/JankyScene.cpp
index f5e6b31..250b986 100644
--- a/libs/hwui/tests/common/scenes/JankyScene.cpp
+++ b/libs/hwui/tests/common/scenes/JankyScene.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 #include <unistd.h>
 
 class JankyScene;
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index 5eaf185..f669dbc 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -17,6 +17,7 @@
 #include "TestSceneBase.h"
 #include "tests/common/TestListViewSceneBase.h"
 #include "hwui/Paint.h"
+#include <SkBlendMode.h>
 #include <SkGradientShader.h>
 
 class ListOfFadedTextAnimation;
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index 402c1ec..1a2af83 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -17,6 +17,8 @@
 #include "TestSceneBase.h"
 #include "utils/Color.h"
 
+#include <SkBlendMode.h>
+
 class OvalAnimation;
 
 static TestScene::Registrar _Oval(TestScene::Info{"oval", "Draws 1 oval.",
diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
index fb1b000..25cf4d6 100644
--- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class PartialDamageAnimation;
 
 static TestScene::Registrar _PartialDamage(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
index 1e343c1..969514c 100644
--- a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include <vector>
 
+#include <SkBlendMode.h>
+
 #include "TestSceneBase.h"
 
 class PathClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index f37bcbc..99e7858 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class RectGridAnimation;
 
 static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
index e9f353d..2c27969 100644
--- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 #include <vector>
 
 class RoundRectClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index 252f539..ee30c13 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -16,6 +16,7 @@
 
 #include <hwui/Paint.h>
 #include <minikin/Layout.h>
+#include <SkBlendMode.h>
 #include <string>
 #include "TestSceneBase.h"
 
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 31a8ae1..d5060c7 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class SaveLayerAnimation;
 
 static TestScene::Registrar _SaveLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
index c13e80e..827ddab 100644
--- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class ShadowGrid2Animation;
 
 static TestScene::Registrar _ShadowGrid2(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
index 772b98e..a4fb10c 100644
--- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class ShadowGridAnimation;
 
 static TestScene::Registrar _ShadowGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
index 0019da5..58c0372 100644
--- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
@@ -16,6 +16,8 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
+
 class ShadowShaderAnimation;
 
 static TestScene::Registrar _ShadowShader(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 70a1557d..c0c3dfd 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -17,6 +17,8 @@
 #include "TestSceneBase.h"
 #include "utils/Color.h"
 
+#include <SkBlendMode.h>
+
 #include <cstdio>
 
 class ShapeAnimation;
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
index 2aeb42c..40f2ed0 100644
--- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -16,6 +16,7 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
 #include <SkColorFilter.h>
 #include <SkColorMatrix.h>
 #include <SkGradientShader.h>
diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
index 57a260c..a9e7a34 100644
--- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
@@ -16,6 +16,7 @@
 
 #include "TestSceneBase.h"
 
+#include <SkBlendMode.h>
 #include <SkGradientShader.h>
 
 class SimpleGradientAnimation;
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index 7d3ca96..bb95490 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <SkBitmap.h>
+#include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkColor.h>
 #include <SkFont.h>
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index d3090367..78146b8 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -17,6 +17,8 @@
 #include "TestSceneBase.h"
 #include "hwui/Paint.h"
 
+#include <SkBlendMode.h>
+
 class TextAnimation;
 
 static TestScene::Registrar _Text(TestScene::Info{"text", "Draws a bunch of text.",
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 9cd1075..a55b725 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -22,6 +22,8 @@
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "tests/common/TestUtils.h"
 
+#include <SkBlendMode.h>
+
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::skiapipeline;
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
index 6aed251..72946c4 100644
--- a/libs/hwui/tests/microbench/RenderNodeBench.cpp
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -19,6 +19,8 @@
 #include "hwui/Canvas.h"
 #include "RenderNode.h"
 
+#include <SkBlendMode.h>
+
 using namespace android;
 using namespace android::uirenderer;
 
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 1771c35..88420a5 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -36,7 +36,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
 
     ASSERT_FALSE(canvasContext->hasSurface());
 
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index d2b1ef9..1f6edf3 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -23,6 +23,7 @@
 
 #include <tests/common/CallCountingCanvas.h>
 
+#include "SkBlendMode.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index ec949b8..596bd37 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -17,6 +17,7 @@
 #include <VectorDrawable.h>
 #include <gtest/gtest.h>
 
+#include <SkBlendMode.h>
 #include <SkClipStack.h>
 #include <SkSurface_Base.h>
 #include <string.h>
@@ -334,7 +335,7 @@
             "A");
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -398,7 +399,7 @@
                                       "A");
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -518,7 +519,7 @@
     // prepareTree is required to find, which receivers have backward projected nodes
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -618,7 +619,7 @@
     // prepareTree is required to find, which receivers have backward projected nodes
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -634,7 +635,7 @@
 static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 61bd646..80796f4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -274,7 +274,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -310,7 +310,7 @@
             });
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
     canvasContext->setSurface(nullptr);
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 50d9f56..87c5216 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -17,10 +17,19 @@
 #include "tests/common/TestUtils.h"
 
 #include <hwui/Paint.h>
+#include <SkAlphaType.h>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
 #include <SkCanvasStateUtils.h>
+#include <SkColor.h>
 #include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <SkImageInfo.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
+#include <SkRefCnt.h>
+#include <SkSurface.h>
 #include <gtest/gtest.h>
 
 using namespace android;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 3d5aca4..f825d7c 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -142,7 +142,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -201,7 +201,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
 
     // Set up a Surface so that we can position the VectorDrawable offscreen.
     test::TestContext testContext;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 7419f8f..4d0595e 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -17,6 +17,7 @@
 #include <VectorDrawable.h>
 #include <gtest/gtest.h>
 
+#include <SkBlendMode.h>
 #include <SkClipStack.h>
 #include <SkSurface_Base.h>
 #include <string.h>
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 94bcb11..f44f9d0 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -19,6 +19,7 @@
 #include <GLES2/gl2.h>
 #include <utils/Blur.h>
 
+#include <SkBlendMode.h>
 #include <SkColorFilter.h>
 #include <SkPaint.h>
 #include <SkShader.h>
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 0e7b7ff..a835167 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -199,8 +199,7 @@
     width = viewport.deviceWidth;
     height = viewport.deviceHeight;
 
-    if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
-        viewport.orientation == DISPLAY_ORIENTATION_270) {
+    if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
         std::swap(width, height);
     }
 }
@@ -244,38 +243,42 @@
 
         // Undo the previous rotation.
         switch (oldViewport.orientation) {
-            case DISPLAY_ORIENTATION_90:
+            case ui::ROTATION_90:
                 temp = x;
                 x = oldViewport.deviceHeight - y;
                 y = temp;
                 break;
-            case DISPLAY_ORIENTATION_180:
+            case ui::ROTATION_180:
                 x = oldViewport.deviceWidth - x;
                 y = oldViewport.deviceHeight - y;
                 break;
-            case DISPLAY_ORIENTATION_270:
+            case ui::ROTATION_270:
                 temp = x;
                 x = y;
                 y = oldViewport.deviceWidth - temp;
                 break;
+            case ui::ROTATION_0:
+                break;
         }
 
         // Perform the new rotation.
         switch (viewport.orientation) {
-            case DISPLAY_ORIENTATION_90:
+            case ui::ROTATION_90:
                 temp = x;
                 x = y;
                 y = viewport.deviceHeight - temp;
                 break;
-            case DISPLAY_ORIENTATION_180:
+            case ui::ROTATION_180:
                 x = viewport.deviceWidth - x;
                 y = viewport.deviceHeight - y;
                 break;
-            case DISPLAY_ORIENTATION_270:
+            case ui::ROTATION_270:
                 temp = x;
                 x = viewport.deviceWidth - y;
                 y = temp;
                 break;
+            case ui::ROTATION_0:
+                break;
         }
 
         // Apply offsets to convert from the pixel center to the pixel top-left corner position
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index 26b190b..62f233e 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -23,6 +24,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -111,6 +114,45 @@
     /** Holds preference information for a specific route in a media routing listing. */
     public static final class Item implements Parcelable {
 
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                flag = true,
+                prefix = {"FLAG_"},
+                value = {FLAG_ONGOING_SESSION, FLAG_SUGGESTED_ROUTE})
+        public @interface Flags {}
+
+        /**
+         * The corresponding route is already hosting a session with the app that owns this listing
+         * preference.
+         */
+        public static final int FLAG_ONGOING_SESSION = 1;
+
+        /**
+         * The corresponding route is specially likely to be selected by the user.
+         *
+         * <p>A UI reflecting this preference may reserve a specific space for suggested routes,
+         * making it more accessible to the user. If the number of suggested routes exceeds the
+         * number supported by the UI, the routes listed first in {@link
+         * RouteListingPreference#getItems()} will take priority.
+         */
+        public static final int FLAG_SUGGESTED_ROUTE = 1 << 1;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                prefix = {"DISABLE_REASON_"},
+                value = {DISABLE_REASON_NONE, DISABLE_REASON_SUBSCRIPTION_REQUIRED})
+        public @interface DisableReason {}
+
+        /** The corresponding route is available for routing. */
+        public static final int DISABLE_REASON_NONE = 0;
+        /**
+         * The corresponding route requires a special subscription in order to be available for
+         * routing.
+         */
+        public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1;
+
         @NonNull
         public static final Creator<Item> CREATOR =
                 new Creator<>() {
@@ -126,21 +168,29 @@
                 };
 
         @NonNull private final String mRouteId;
+        @Flags private final int mFlags;
+        @DisableReason private final int mDisableReason;
 
         /**
          * Creates an instance with the given value.
          *
          * @param routeId See {@link #getRouteId()}. Must not be empty.
+         * @param flags See {@link #getFlags()}.
+         * @param disableReason See {@link #getDisableReason()}.
          */
-        public Item(@NonNull String routeId) {
+        public Item(@NonNull String routeId, @Flags int flags, @DisableReason int disableReason) {
             Preconditions.checkArgument(!TextUtils.isEmpty(routeId));
             mRouteId = routeId;
+            mFlags = flags;
+            mDisableReason = disableReason;
         }
 
         private Item(Parcel in) {
             String routeId = in.readString();
             Preconditions.checkArgument(!TextUtils.isEmpty(routeId));
             mRouteId = routeId;
+            mFlags = in.readInt();
+            mDisableReason = in.readInt();
         }
 
         /** Returns the id of the route that corresponds to this route listing preference item. */
@@ -149,6 +199,29 @@
             return mRouteId;
         }
 
+        /**
+         * Returns the flags associated to the route that corresponds to this item.
+         *
+         * @see #FLAG_ONGOING_SESSION
+         * @see #FLAG_SUGGESTED_ROUTE
+         */
+        @Flags
+        public int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Returns the reason for the corresponding route to be disabled, or {@link
+         * #DISABLE_REASON_NONE} if the route is not disabled.
+         *
+         * @see #DISABLE_REASON_NONE
+         * @see #DISABLE_REASON_SUBSCRIPTION_REQUIRED
+         */
+        @DisableReason
+        public int getDisableReason() {
+            return mDisableReason;
+        }
+
         // Item Parcelable implementation.
 
         @Override
@@ -159,6 +232,8 @@
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeString(mRouteId);
+            dest.writeInt(mFlags);
+            dest.writeInt(mDisableReason);
         }
 
         // Equals and hashCode.
@@ -172,12 +247,14 @@
                 return false;
             }
             Item item = (Item) other;
-            return mRouteId.equals(item.mRouteId);
+            return mRouteId.equals(item.mRouteId)
+                    && mFlags == item.mFlags
+                    && mDisableReason == item.mDisableReason;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mRouteId);
+            return Objects.hash(mRouteId, mFlags, mDisableReason);
         }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index 15811d2..9144087 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -38,7 +38,8 @@
                     VIDEO_STREAM_TYPE_MPEG1, VIDEO_STREAM_TYPE_MPEG2,
                     VIDEO_STREAM_TYPE_MPEG4P2, VIDEO_STREAM_TYPE_AVC, VIDEO_STREAM_TYPE_HEVC,
                     VIDEO_STREAM_TYPE_VC1, VIDEO_STREAM_TYPE_VP8, VIDEO_STREAM_TYPE_VP9,
-                    VIDEO_STREAM_TYPE_AV1, VIDEO_STREAM_TYPE_AVS, VIDEO_STREAM_TYPE_AVS2})
+                    VIDEO_STREAM_TYPE_AV1, VIDEO_STREAM_TYPE_AVS, VIDEO_STREAM_TYPE_AVS2,
+                    VIDEO_STREAM_TYPE_VVC})
     @Retention(RetentionPolicy.SOURCE)
     public @interface VideoStreamType {}
 
@@ -76,6 +77,10 @@
      */
     public static final int VIDEO_STREAM_TYPE_HEVC = android.hardware.tv.tuner.VideoStreamType.HEVC;
     /*
+     * ITU-T Rec. H.266 and ISO/IEC 23090-3
+     */
+    public static final int VIDEO_STREAM_TYPE_VVC = android.hardware.tv.tuner.VideoStreamType.VVC;
+    /*
      * Microsoft VC.1
      */
     public static final int VIDEO_STREAM_TYPE_VC1 = android.hardware.tv.tuner.VideoStreamType.VC1;
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index d0973f4..8568c43 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -349,6 +349,15 @@
                         + mMainType + ", filter subtype=" + mSubtype + ". config main type="
                         + config.getType() + ", config subtype=" + subType);
             }
+            // Tuner only support VVC after tuner 3.0
+            if (s instanceof RecordSettings
+                    && ((RecordSettings) s).getScIndexType() == RecordSettings.INDEX_TYPE_SC_VVC
+                    && !TunerVersionChecker.isHigherOrEqualVersionTo(
+                            TunerVersionChecker.TUNER_VERSION_3_0)) {
+                Log.e(TAG, "Tuner version " + TunerVersionChecker.getTunerVersion()
+                        + " does not support VVC");
+                return Tuner.RESULT_UNAVAILABLE;
+            }
             return nativeConfigureFilter(config.getType(), subType, config);
         }
     }
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index b16d9fb..698bbba 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -23,8 +23,10 @@
 import android.hardware.tv.tuner.DemuxScAvcIndex;
 import android.hardware.tv.tuner.DemuxScHevcIndex;
 import android.hardware.tv.tuner.DemuxScIndex;
+import android.hardware.tv.tuner.DemuxScVvcIndex;
 import android.hardware.tv.tuner.DemuxTsIndex;
 import android.media.tv.tuner.TunerUtils;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -138,7 +140,8 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "INDEX_TYPE_", value =
-            {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC, INDEX_TYPE_SC_AVC})
+            {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC, INDEX_TYPE_SC_AVC,
+             INDEX_TYPE_SC_VVC})
     public @interface ScIndexType {}
 
     /**
@@ -157,6 +160,10 @@
      * Start Code index for AVC.
      */
     public static final int INDEX_TYPE_SC_AVC = DemuxRecordScIndexType.SC_AVC;
+    /**
+     * Start Code index for VVC.
+     */
+    public static final int INDEX_TYPE_SC_VVC = DemuxRecordScIndexType.SC_VVC;
 
     /**
      * Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
@@ -253,6 +260,46 @@
     public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = DemuxScHevcIndex.SLICE_TRAIL_CRA;
 
     /**
+     * Indexes can be tagged by NAL unit group in VVC according to ISO/IEC 23090-3.
+     *
+     * @hide
+     */
+    @IntDef(value = {SC_VVC_INDEX_SLICE_IDR_W_RADL, SC_VVC_INDEX_SLICE_IDR_N_LP,
+            SC_VVC_INDEX_SLICE_CRA, SC_VVC_INDEX_SLICE_GDR, SC_VVC_INDEX_VPS, SC_VVC_INDEX_SPS,
+            SC_VVC_INDEX_AUD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScVvcIndex{}
+
+    /**
+     * SC VVC index SLICE_IDR_W_RADL (nal_unit_type=IDR_W_RADL) for random access key frame.
+     */
+    public static final int SC_VVC_INDEX_SLICE_IDR_W_RADL = DemuxScVvcIndex.SLICE_IDR_W_RADL;
+    /**
+     * SC VVC index SLICE_IDR_N_LP (nal_unit_type=IDR_N_LP) for random access key frame.
+     */
+    public static final int SC_VVC_INDEX_SLICE_IDR_N_LP = DemuxScVvcIndex.SLICE_IDR_N_LP;
+    /**
+     * SC VVC index SLICE_CRA (nal_unit_type=CRA_NUT) for random access key frame.
+     */
+    public static final int SC_VVC_INDEX_SLICE_CRA = DemuxScVvcIndex.SLICE_CRA;
+    /**
+     * SC VVC index SLICE_GDR (nal_unit_type=GDR_NUT) for random access point.
+     */
+    public static final int SC_VVC_INDEX_SLICE_GDR = DemuxScVvcIndex.SLICE_GDR;
+    /**
+     * Optional SC VVC index VPS (nal_unit_type=VPS_NUT) for sequence level info.
+     */
+    public static final int SC_VVC_INDEX_VPS = DemuxScVvcIndex.VPS;
+    /**
+     * SC VVC index SPS (nal_unit_type=SPS_NUT) for sequence level info.
+     */
+    public static final int SC_VVC_INDEX_SPS = DemuxScVvcIndex.SPS;
+    /**
+     * SC VVC index AUD (nal_unit_type=AUD_NUT) for AU (frame) boundary.
+     */
+    public static final int SC_VVC_INDEX_AUD = DemuxScVvcIndex.AUD;
+
+    /**
      * @hide
      */
     @IntDef(prefix = "SC_",
@@ -261,6 +308,11 @@
                 SC_INDEX_P_FRAME,
                 SC_INDEX_B_FRAME,
                 SC_INDEX_SEQUENCE,
+                SC_INDEX_I_SLICE,
+                SC_INDEX_P_SLICE,
+                SC_INDEX_B_SLICE,
+                SC_INDEX_SI_SLICE,
+                SC_INDEX_SP_SLICE,
                 SC_HEVC_INDEX_SPS,
                 SC_HEVC_INDEX_AUD,
                 SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
@@ -269,6 +321,13 @@
                 SC_HEVC_INDEX_SLICE_IDR_W_RADL,
                 SC_HEVC_INDEX_SLICE_IDR_N_LP,
                 SC_HEVC_INDEX_SLICE_TRAIL_CRA,
+                SC_VVC_INDEX_SLICE_IDR_W_RADL,
+                SC_VVC_INDEX_SLICE_IDR_N_LP,
+                SC_VVC_INDEX_SLICE_CRA,
+                SC_VVC_INDEX_SLICE_GDR,
+                SC_VVC_INDEX_VPS,
+                SC_VVC_INDEX_SPS,
+                SC_VVC_INDEX_AUD
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ScIndexMask {}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 2afa4d1..58078cf 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -661,6 +661,8 @@
         sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scAvc>();
         // Java uses the values defined by HIDL HAL. Left shift 4 bits.
         sc = sc << 4;
+    } else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) {
+        sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
     }
 
     jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
@@ -726,6 +728,8 @@
         sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scAvc>();
         // Java uses the values defined by HIDL HAL. Left shift 4 bits.
         sc = sc << 4;
+    } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) {
+        sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
     }
 
     jint ts = tsRecordEvent.tsIndexMask;
@@ -3819,6 +3823,8 @@
     } else if (scIndexType == DemuxRecordScIndexType::SC_AVC) {
         // Java uses the values defined by HIDL HAL. Right shift 4 bits.
         filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scAvc>(scIndexMask >> 4);
+    } else if (scIndexType == DemuxRecordScIndexType::SC_VVC) {
+        filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scVvc>(scIndexMask);
     }
     return filterRecordSettings;
 }
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 7863a7d..40eb507 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -71,8 +71,10 @@
     const int64_t mPreferredRateNanos;
     // Target duration for choosing update rate
     int64_t mTargetDurationNanos;
-    // Last update timestamp
-    int64_t mLastUpdateTimestamp;
+    // First target hit timestamp
+    int64_t mFirstTargetMetTimestamp;
+    // Last target hit timestamp
+    int64_t mLastTargetMetTimestamp;
     // Cached samples
     std::vector<int64_t> mActualDurationsNanos;
     std::vector<int64_t> mTimestampsNanos;
@@ -144,7 +146,8 @@
       : mHintSession(std::move(session)),
         mPreferredRateNanos(preferredRateNanos),
         mTargetDurationNanos(targetDurationNanos),
-        mLastUpdateTimestamp(elapsedRealtimeNano()) {}
+        mFirstTargetMetTimestamp(0),
+        mLastTargetMetTimestamp(0) {}
 
 APerformanceHintSession::~APerformanceHintSession() {
     binder::Status ret = mHintSession->close();
@@ -171,7 +174,8 @@
      */
     mActualDurationsNanos.clear();
     mTimestampsNanos.clear();
-    mLastUpdateTimestamp = elapsedRealtimeNano();
+    mFirstTargetMetTimestamp = 0;
+    mLastTargetMetTimestamp = 0;
     return 0;
 }
 
@@ -184,25 +188,38 @@
     mActualDurationsNanos.push_back(actualDurationNanos);
     mTimestampsNanos.push_back(now);
 
-    /**
-     * Cache the hint if the hint is not overtime and the mLastUpdateTimestamp is
-     * still in the mPreferredRateNanos duration.
-     */
-    if (actualDurationNanos < mTargetDurationNanos &&
-        now - mLastUpdateTimestamp <= mPreferredRateNanos) {
-        return 0;
+    if (actualDurationNanos >= mTargetDurationNanos) {
+        // Reset timestamps if we are equal or over the target.
+        mFirstTargetMetTimestamp = 0;
+    } else {
+        // Set mFirstTargetMetTimestamp for first time meeting target.
+        if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
+            (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
+            mFirstTargetMetTimestamp = now;
+        }
+        /**
+         * Rate limit the change if the update is over mPreferredRateNanos since first
+         * meeting target and less than mPreferredRateNanos since last meeting target.
+         */
+        if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
+            now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
+            return 0;
+        }
+        mLastTargetMetTimestamp = now;
     }
 
     binder::Status ret =
             mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
-    mActualDurationsNanos.clear();
-    mTimestampsNanos.clear();
     if (!ret.isOk()) {
         ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
               ret.exceptionMessage().c_str());
+        mFirstTargetMetTimestamp = 0;
+        mLastTargetMetTimestamp = 0;
         return EPIPE;
     }
-    mLastUpdateTimestamp = now;
+    mActualDurationsNanos.clear();
+    mTimestampsNanos.clear();
+
     return 0;
 }
 
diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml
new file mode 100644
index 0000000..91771b3
--- /dev/null
+++ b/packages/CredentialManager/res/values-af/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Kanselleer"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Gaan voort"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Skep op ’n ander plek"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Stoor in ’n ander plek"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Gebruik ’n ander toestel"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Stoor op ’n ander toestel"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"’n Maklike manier om veilig aan te meld"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gebruik jou vingerafdruk, gesig of skermslot om aan te meld met ’n unieke wagwoordsleutel wat nie vergeet of gesteel kan word nie. Kom meer te wete"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Kies waar om <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"stoor jou wagwoord"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"stoor jou aanmeldinligting"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Skep ’n wagwoordsleutel in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Stoor jou wagwoord in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Stoor jou aanmeldinligting in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Jy kan jou <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> op enige toestel gebruik. Dit is in <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> gestoor vir <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"wagwoordsleutel"</string>
+    <string name="password" msgid="6738570945182936667">"wagwoord"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"aanmeldings"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gebruik <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> vir al jou aanmeldings?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Stel as verstek"</string>
+    <string name="use_once" msgid="9027366575315399714">"Gebruik een keer"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> wagwoordsleutels"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> wagwoordsleutels"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"’n Ander toestel"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Ander wagwoordbestuurders"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Maak sigblad toe"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gaan terug na die vorige bladsy"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gebruik jou gestoorde wagwoordsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gebruik jou gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Kies ’n gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Meld op ’n ander manier aan"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nee, dankie"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Gaan voort"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Aanmeldopsies"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Vir <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Geslote wagwoordbestuurders"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tik om te ontsluit"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Bestuur aanmeldings"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Van ’n ander toestel af"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gebruik ’n ander toestel"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml
new file mode 100644
index 0000000..e77b1a7
--- /dev/null
+++ b/packages/CredentialManager/res/values-am/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <!-- no translation found for string_cancel (6369133483981306063) -->
+    <skip />
+    <!-- no translation found for string_continue (1346732695941131882) -->
+    <skip />
+    <!-- no translation found for string_create_in_another_place (1033635365843437603) -->
+    <skip />
+    <!-- no translation found for string_save_to_another_place (7590325934591079193) -->
+    <skip />
+    <!-- no translation found for string_use_another_device (8754514926121520445) -->
+    <skip />
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"ወደ ሌላ መሣሪያ ያስቀምጡ"</string>
+    <!-- no translation found for passkey_creation_intro_title (402553911484409884) -->
+    <skip />
+    <!-- no translation found for passkey_creation_intro_body (7493320456005579290) -->
+    <skip />
+    <!-- no translation found for choose_provider_title (7245243990139698508) -->
+    <skip />
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <!-- no translation found for save_your_password (6597736507991704307) -->
+    <skip />
+    <!-- no translation found for save_your_sign_in_info (7213978049817076882) -->
+    <skip />
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <!-- no translation found for choose_create_option_passkey_title (4146408187146573131) -->
+    <skip />
+    <!-- no translation found for choose_create_option_password_title (8812546498357380545) -->
+    <skip />
+    <!-- no translation found for choose_create_option_sign_in_title (6318246378475961834) -->
+    <skip />
+    <!-- no translation found for choose_create_option_description (4419171903963100257) -->
+    <skip />
+    <!-- no translation found for passkey (632353688396759522) -->
+    <skip />
+    <!-- no translation found for password (6738570945182936667) -->
+    <skip />
+    <!-- no translation found for sign_ins (4710739369149469208) -->
+    <skip />
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <!-- no translation found for use_provider_for_all_title (4201020195058980757) -->
+    <skip />
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <!-- no translation found for set_as_default (4415328591568654603) -->
+    <skip />
+    <!-- no translation found for use_once (9027366575315399714) -->
+    <skip />
+    <!-- no translation found for more_options_usage_passwords_passkeys (4794903978126339473) -->
+    <skip />
+    <!-- no translation found for more_options_usage_passwords (1632047277723187813) -->
+    <skip />
+    <!-- no translation found for more_options_usage_passkeys (5390320437243042237) -->
+    <skip />
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <!-- no translation found for another_device (5147276802037801217) -->
+    <skip />
+    <!-- no translation found for other_password_manager (565790221427004141) -->
+    <skip />
+    <!-- no translation found for close_sheet (1393792015338908262) -->
+    <skip />
+    <!-- no translation found for accessibility_back_arrow_button (3233198183497842492) -->
+    <skip />
+    <!-- no translation found for get_dialog_title_use_passkey_for (6236608872708021767) -->
+    <skip />
+    <!-- no translation found for get_dialog_title_use_sign_in_for (5283099528915572980) -->
+    <skip />
+    <!-- no translation found for get_dialog_title_choose_sign_in_for (1361715440877613701) -->
+    <skip />
+    <!-- no translation found for get_dialog_use_saved_passkey_for (4618100798664888512) -->
+    <skip />
+    <!-- no translation found for get_dialog_button_label_no_thanks (8114363019023838533) -->
+    <skip />
+    <!-- no translation found for get_dialog_button_label_continue (6446201694794283870) -->
+    <skip />
+    <!-- no translation found for get_dialog_title_sign_in_options (2092876443114893618) -->
+    <skip />
+    <!-- no translation found for get_dialog_heading_for_username (3456868514554204776) -->
+    <skip />
+    <!-- no translation found for get_dialog_heading_locked_password_managers (8911514851762862180) -->
+    <skip />
+    <!-- no translation found for locked_credential_entry_label_subtext (9213450912991988691) -->
+    <skip />
+    <!-- no translation found for get_dialog_heading_manage_sign_ins (3522556476480676782) -->
+    <skip />
+    <!-- no translation found for get_dialog_heading_from_another_device (1166697017046724072) -->
+    <skip />
+    <!-- no translation found for get_dialog_option_headline_use_a_different_device (8201578814988047549) -->
+    <skip />
+</resources>
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
new file mode 100644
index 0000000..4af875c
--- /dev/null
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"إلغاء"</string>
+    <string name="string_continue" msgid="1346732695941131882">"متابعة"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"الإنشاء في مكان آخر"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"الحفظ في مكان آخر"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"استخدام جهاز آخر"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"طريقة بسيطة لتسجيل الدخول بأمان"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"استخدِم بصمة إصبعك أو وجهك أو قفل الشاشة لتسجيل الدخول باستخدام مفتاح مرور فريد لا يمكن نسيانه أو سرقته. مزيد من المعلومات"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"اختيار مكان <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"حفظ كلمة المرور"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"حفظ معلومات تسجيل الدخول"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"هل تريد إنشاء مفتاح مرور في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"؟"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"هل تريد حفظ كلمة مرورك في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"؟"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"هل تريد حفظ معلومات تسجيل الدخول في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"؟"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"يمكنك استخدام <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> على أي جهاز. يتم حفظه في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\" لـ \"<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>\"."</string>
+    <string name="passkey" msgid="632353688396759522">"مفتاح مرور"</string>
+    <string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"عمليات تسجيل الدخول"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"هل تريد استخدام \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" لكل عمليات تسجيل الدخول؟"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ضبط الخيار كتلقائي"</string>
+    <string name="use_once" msgid="9027366575315399714">"الاستخدام مرة واحدة"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"عدد كلمات المرور هو <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>، و عدد مفاتيح المرور هو <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"عدد كلمات المرور: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"عدد مفاتيح المرور: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"جهاز آخر"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"خدمات مدراء كلمات المرور الأخرى"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"إغلاق ورقة البيانات"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"العودة إلى الصفحة السابقة"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"هل تريد استخدام مفتاح المرور المحفوظ لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"هل تريد استخدام بيانات اعتماد تسجيل الدخول المحفوظة لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"اختيار بيانات اعتماد تسجيل دخول محفوظة لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"تسجيل الدخول بطريقة أخرى"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"لا، شكرًا"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"متابعة"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"خيارات تسجيل الدخول"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"معلومات تسجيل دخول \"<xliff:g id="USERNAME">%1$s</xliff:g>\""</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"خدمات إدارة كلمات المرور المقفولة"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"انقر لإلغاء القفل."</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"إداراة عمليات تسجيل الدخول"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"من جهاز آخر"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استخدام جهاز مختلف"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml
new file mode 100644
index 0000000..c104098
--- /dev/null
+++ b/packages/CredentialManager/res/values-as/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"বাতিল কৰক"</string>
+    <string name="string_continue" msgid="1346732695941131882">"অব্যাহত ৰাখক"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"অন্য ঠাইত সৃষ্টি কৰক"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"অন্য ঠাইত ছেভ কৰক"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"অন্য ডিভাইচ ব্যৱহাৰ কৰক"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"সুৰক্ষিতভাৱে ছাইন ইন কৰাৰ এক সৰল উপায়"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"পাহৰি নোযোৱা অথবা চুৰি কৰিব নোৱৰা এটা অদ্বিতীয় পাছকী ব্যৱহাৰ কৰি ছাইন ইন কৰিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট, মুখাৱয়ব অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক। অধিক জানক"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"ক’ত <xliff:g id="CREATETYPES">%1$s</xliff:g> সেয়া বাছনি কৰক"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"আপোনাৰ পাছৱৰ্ড ছেভ কৰক"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"আপোনাৰ ছাইন ইন কৰাৰ তথ্য ছেভ কৰক"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ত পাছকী সৃষ্টি কৰিবনে?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ত আপোনাৰ পাছৱৰ্ড ছেভ কৰিবনে?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ত আপোনাৰ ছাইন ইন কৰাৰ তথ্য ছেভ কৰিবনে?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"আপুনি আপোনাৰ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> যিকোনো ডিভাইচত ব্যৱহাৰ কৰিব পাৰে। এইটো <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>ৰ বাবে <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>ত ছেভ কৰা হৈছে"</string>
+    <string name="passkey" msgid="632353688396759522">"পাছকী"</string>
+    <string name="password" msgid="6738570945182936667">"পাছৱৰ্ড"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ছাইন-ইন"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"আপোনাৰ আটাইবোৰ ছাইন ইনৰ বাবে <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবনে?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ডিফ’ল্ট হিচাপে ছেট কৰক"</string>
+    <string name="use_once" msgid="9027366575315399714">"এবাৰ ব্যৱহাৰ কৰক"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> টা পাছৱৰ্ড, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> টা পাছকী"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> টা পাছৱৰ্ড"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> টা পাছকী"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"অন্য এটা ডিভাইচ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"অন্য পাছৱৰ্ড পৰিচালক"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"শ্বীট বন্ধ কৰক"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"পূৰ্বৱৰ্তী পৃষ্ঠালৈ ঘূৰি যাওক"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে আপোনাৰ ছেভ হৈ থকা পাছকী ব্যৱহাৰ কৰিবনে?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে আপোনাৰ ছেভ হৈ থকা ছাইন ইন তথ্য ব্যৱহাৰ কৰিবনে?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে ছেভ হৈ থকা এটা ছাইন ইন বাছনি কৰক"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"অন্য উপায়েৰে ছাইন ইন কৰক"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"নালাগে, ধন্যবাদ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"অব্যাহত ৰাখক"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ছাইন ইনৰ বিকল্প"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>ৰ বাবে"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"লক হৈ থকা পাছৱৰ্ড পৰিচালক"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"আনলক কৰিবলৈ টিপক"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ছাইন ইন পৰিচালনা কৰক"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য এটা ডিভাইচৰ পৰা"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"অন্য এটা ডিভাইচ ব্যৱহাৰ কৰক"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml
new file mode 100644
index 0000000..4a8fef5
--- /dev/null
+++ b/packages/CredentialManager/res/values-az/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Ləğv edin"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Davam edin"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Başqa yerdə yaradın"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Başqa yerdə yadda saxlayın"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Digər cihaz istifadə edin"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Başqa cihazda yadda saxlayın"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Təhlükəsiz daxil olmağın sadə yolu"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Unutmaq və ya oğurlamaq mümkün olmayan unikal giriş açarı ilə daxil olmaq üçün barmaq izi, üz və ya ekran kilidindən istifadə edin. Ətraflı məlumat"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> üçün yer seçin"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"parolunuzu yadda saxlayın"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"giriş məlumatınızı yadda saxlayın"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xidmətində giriş açarı yaradılsın?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Parol <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xidmətində saxlanılsın?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Giriş məlumatınız <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xidmətində saxlanılsın?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> domenini istənilən cihazda istifadə edə bilərsiniz. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> üçün <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> xidmətində saxlanılıb"</string>
+    <string name="passkey" msgid="632353688396759522">"giriş açarı"</string>
+    <string name="password" msgid="6738570945182936667">"parol"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"girişlər"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Bütün girişlər üçün <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> istifadə edilsin?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Defolt olaraq seçin"</string>
+    <string name="use_once" msgid="9027366575315399714">"Bir dəfə istifadə edin"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parol, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> giriş açarı"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parol"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> giriş açarı"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Digər cihaz"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Digər parol menecerləri"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Səhifəni bağlayın"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Əvvəlki səhifəyə qayıdın"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış giriş açarı istifadə edilsin?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış girişdən istifadə edilsin?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış girişi seçin"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Başqa üsulla daxil olun"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Xeyr, təşəkkürlər"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Davam edin"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Giriş seçimləri"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> üçün"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Kilidli parol menecerləri"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Kilidi açmaq üçün tıklayın"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Girişləri idarə edin"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başqa cihazdan"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Başqa cihaz istifadə edin"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..b46518e
--- /dev/null
+++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Napravi na drugom mestu"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Sačuvaj na drugom mestu"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Koristi drugi uređaj"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Sačuvaj na drugi uređaj"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednostavan način da se bezbedno prijavljujete"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Koristite otisak prsta, zaključavanje licem ili zaključavanje ekrana da biste se prijavili pomoću jedinstvenog pristupnog koda koji ne može da se zaboravi ili ukrade. Saznajte više"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite lokaciju za: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"sačuvajte lozinku"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"sačuvajte podatke o prijavljivanju"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite da napravite pristupni kôd kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite da sačuvate lozinku kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite da sačuvate podatke o prijavljivanju kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Možete da koristite tip domena <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> na bilo kom uređaju. Čuva se kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"pristupni kôd"</string>
+    <string name="password" msgid="6738570945182936667">"lozinka"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"prijavljivanja"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite da za sva prijavljivanja koristite: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Podesi kao podrazumevano"</string>
+    <string name="use_once" msgid="9027366575315399714">"Koristi jednom"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, pristupnih kodova:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Pristupnih kodova: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Drugi menadžeri lozinki"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zatvorite tabelu"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite da koristite sačuvani pristupni kôd za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite da koristite sačuvane podatke za prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite sačuvano prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na drugi način"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije za prijavljivanje"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menadžeri zaključanih lozinki"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite da biste otključali"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavljivanjima"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Sa drugog uređaja"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Koristi drugi uređaj"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml
new file mode 100644
index 0000000..afa4d01
--- /dev/null
+++ b/packages/CredentialManager/res/values-be/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Скасаваць"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Далей"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Стварыць у іншым месцы"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Захаваць у іншым месцы"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Скарыстаць іншую прыладу"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Захаваць на іншую прыладу"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Просты спосаб бяспечнага ўваходу"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Для ўваходу з унікальным ключом доступу, які нельга згубіць ці ўкрасці, можна скарыстаць адбітак пальца, распазнаванне твару ці разблакіроўку экрана. Даведацца больш"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Выберыце, дзе <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"захаваць пароль"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"захаваць інфармацыю пра спосаб уваходу"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Стварыць ключ доступу ў папцы \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Захаваць пароль у папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Захаваць інфармацыю пра спосаб уваходу ў папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Вы можаце выкарыстоўваць <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на любой прыладзе. Даныя для \"<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>\" захоўваюцца ў папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\""</string>
+    <string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
+    <string name="password" msgid="6738570945182936667">"пароль"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"спосабы ўваходу"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Выкарыстоўваць папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" для ўсіх спосабаў уваходу?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Выкарыстоўваць стандартна"</string>
+    <string name="use_once" msgid="9027366575315399714">"Скарыстаць адзін раз"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Пароляў: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, ключоў доступу: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Пароляў: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Ключоў доступу: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Іншая прылада"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Іншыя спосабы ўваходу"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Закрыць аркуш"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вярнуцца да папярэдняй старонкі"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Скарыстаць захаваны ключ доступу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Скарыстаць захаваныя спосабы ўваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Выберыце захаваны спосаб уваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Увайсці іншым спосабам"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, дзякуй"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Далей"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Спосабы ўваходу"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблакіраваныя спосабы ўваходу"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Націсніце, каб разблакіраваць"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіраваць спосабамі ўваходу"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншай прылады"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Скарыстаць іншую прыладу"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml
new file mode 100644
index 0000000..1a2f881
--- /dev/null
+++ b/packages/CredentialManager/res/values-bg/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Отказ"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Напред"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Създаване другаде"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Запазване на друго място"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Използване на друго устройство"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Запазване на друго устройство"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Лесен начин за безопасно влизане в профил"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Използвайте отпечатъка, лицето или опцията си за заключване на екрана, за да влизате в профила си с помощта на уникален код за достъп, който не може да бъде забравен или откраднат. Научете повече"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Изберете място за <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"запазване на паролата ви"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"запазване на данните ви за вход"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Да се създаде ли код за достъп в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Искате ли да запазите паролата си в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Искате ли да запазите данните си за вход в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Можете да използвате <xliff:g id="TYPE">%2$s</xliff:g> за <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> на всяко устройство. Тези данни се запазват в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"код за достъп"</string>
+    <string name="password" msgid="6738570945182936667">"парола"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"данни за вход"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Да се използва ли <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> за всичките ви данни за вход?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Задаване като основно"</string>
+    <string name="use_once" msgid="9027366575315399714">"Еднократно използване"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> пароли, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> кода за достъп"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> пароли"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> кода за достъп"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Друго устройство"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Други мениджъри на пароли"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Затваряне на таблицата"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Назад към предишната страница"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се използва ли запазеният ви код за достъп за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се използват ли запазените ви данни за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Изберете запазени данни за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Влизане в профила по друг начин"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, благодаря"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Напред"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опции за влизане в профила"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заключени мениджъри на пароли"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Докоснете, за да отключите"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управление на данните за вход"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"От друго устройство"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Използване на друго устройство"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml
new file mode 100644
index 0000000..3257b78
--- /dev/null
+++ b/packages/CredentialManager/res/values-bn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"বাতিল করুন"</string>
+    <string name="string_continue" msgid="1346732695941131882">"চালিয়ে যান"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"অন্য জায়গায় তৈরি করুন"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"অন্য জায়গায় সেভ করুন"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"অন্য ডিভাইস ব্যবহার করুন"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"অন্য ডিভাইসে সেভ করুন"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"নিরাপদে সাইন-ইন করার একটি সহজ উপায়"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"একটি অনন্য পাসকী ব্যবহার করে সাইন-ইন করতে আপনার ফিঙ্গারপ্রিন্ট, মুখ বা স্ক্রিন লক ব্যবহার করুন যেটি ভুলে যাবেন না বা হারিয়ে যাবেন না। আরও জানুন"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> কোথায় সেভ করবেন তা বেছে নিন"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"আপনার পাসওয়ার্ড সেভ করুন"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"আপনার সাইন-ইন করা সম্পর্কিত তথ্য সেভ করুন"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-এ পাসকী তৈরি করবেন?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"আপনার পাসওয়ার্ড <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-এ সেভ করবেন?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"আপনার সাইন-ইন করা সম্পর্কিত তথ্য <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-এ সেভ করবেন?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"যেকোনও ডিভাইসে নিজের <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ব্যবহার করতে পারবেন। এটি <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-এর জন্য <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>-এ সেভ করা আছে"</string>
+    <string name="passkey" msgid="632353688396759522">"পাসকী"</string>
+    <string name="password" msgid="6738570945182936667">"পাসওয়ার্ড"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"সাইন-ইন"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"আপনার সব সাইন-ইনের জন্য <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ব্যবহার করবেন?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ডিফল্ট হিসেবে সেট করুন"</string>
+    <string name="use_once" msgid="9027366575315399714">"একবার ব্যবহার করুন"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>টি পাসওয়ার্ড, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>টি পাসকী"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>টি পাসওয়ার্ড"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>টি পাসকী"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"অন্য ডিভাইস"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"অন্যান্য Password Manager"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"শিট বন্ধ করুন"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"আগের পৃষ্ঠায় ফিরে যান"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা পাসকী ব্যবহার করবেন?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা সাইন-ইন সম্পর্কিত ক্রেডেনশিয়াল ব্যবহার করবেন?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য সাইন-ইন করা সম্পর্কিত ক্রেডেনশিয়াল বেছে নিন"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"অন্যভাবে সাইন-ইন করুন"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"না থাক"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"চালিয়ে যান"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"সাইন-ইন করার বিকল্প"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-এর জন্য"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"লক করা Password Manager"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"আনলক করতে ট্যাপ করুন"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"সাইন-ইন করার ক্রেডেনশিয়াল ম্যানেজ করুন"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য ডিভাইস থেকে"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"আলাদা ডিভাইস ব্যবহার করুন"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml
new file mode 100644
index 0000000..705cfef
--- /dev/null
+++ b/packages/CredentialManager/res/values-bs/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Kreirajte na drugom mjestu"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Sačuvajte na drugom mjestu"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Koristite drugi uređaj"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Sačuvajte na drugom uređaju"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednostavan način za sigurnu prijavu"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Koristite otisak prsta, lice ili zaključavanje ekrana da se prijavite jedinstvenim pristupnim ključem koji se ne može zaboraviti niti ukrasti. Saznajte više"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite gdje <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"sačuvajte lozinku"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"sačuvajte informacije za prijavu"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Kreirati pristupni ključ na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Sačuvati lozinku na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Sačuvati informacije za prijavu na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Možete koristiti <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> na bilo kojem uređaju. Sačuvan je na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
+    <string name="password" msgid="6738570945182936667">"lozinka"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Koristiti uslugu <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> za sve vaše prijave?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Postavi kao zadano"</string>
+    <string name="use_once" msgid="9027366575315399714">"Koristi jednom"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Broj lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>; broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Broj lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji lozinki"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zatvaranje tabele"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Povratak na prethodnu stranicu"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Koristiti sačuvani pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Koristiti sačuvanu prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite sačuvanu prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na drugi način"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije prijave"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za osobu <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zaključani upravitelji lozinki"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite da otključate"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavama"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"S drugog uređaja"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml
new file mode 100644
index 0000000..d9b5a90
--- /dev/null
+++ b/packages/CredentialManager/res/values-ca/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancel·la"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continua"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Crea en un altre lloc"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Desa en un altre lloc"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Utilitza un altre dispositiu"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Una manera senzilla i segura d\'iniciar la sessió"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Utilitza l\'empremta digital, la cara o el bloqueig de pantalla per iniciar la sessió amb una clau d\'accés única que no es pot oblidar ni robar. Més informació"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Tria on vols <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"desar la contrasenya"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"desar la teva informació d\'inici de sessió"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vols crear una clau d\'accés a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vols desar la contrasenya a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vols desar la teva informació d\'inici de sessió a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Pots utilitzar <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> en qualsevol dispositiu. Està desat a <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> per a <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"clau d\'accés"</string>
+    <string name="password" msgid="6738570945182936667">"contrasenya"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"inicis de sessió"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vols utilitzar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> per a tots els teus inicis de sessió?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Estableix com a predeterminada"</string>
+    <string name="use_once" msgid="9027366575315399714">"Utilitza un cop"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasenyes, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> claus d\'accés"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasenyes"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> claus d\'accés"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Un altre dispositiu"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Altres gestors de contrasenyes"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Tanca el full"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Torna a la pàgina anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vols utilitzar la clau d\'accés desada per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vols utilitzar l\'inici de sessió desat per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Tria un inici de sessió desat per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Inicia la sessió d\'una altra manera"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, gràcies"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continua"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcions d\'inici de sessió"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Per a <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestors de contrasenyes bloquejats"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toca per desbloquejar"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestiona els inicis de sessió"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Des d\'un altre dispositiu"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utilitza un dispositiu diferent"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml
new file mode 100644
index 0000000..e7fe5365
--- /dev/null
+++ b/packages/CredentialManager/res/values-cs/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Zrušit"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Pokračovat"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Vytvořit na jiném místě"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Uložit na jiné místo"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Použít jiné zařízení"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Uložit do jiného zařízení"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednoduchý způsob, jak se bezpečně přihlásit"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Použijte svůj otisk prstu, obličej nebo zámek obrazovky k přihlášení pomocí jedinečného přístupového klíče, který nemůžete zapomenout a který vám nikdo nemůže odcizit. Další informace"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Zvolte, kde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"uložte si heslo"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"uložte své přihlašovací údaje"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vytvořit přístupový klíč v <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Uložit heslo do <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Uložit přihlašovací údaje do <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Svoje <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> můžete používat na libovolném zařízení. Ukládá se do <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pro <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"přístupový klíč"</string>
+    <string name="password" msgid="6738570945182936667">"heslo"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"přihlášení"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Používat <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pro všechna přihlášení?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Nastavit jako výchozí"</string>
+    <string name="use_once" msgid="9027366575315399714">"Použít jednou"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Počet hesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, počet přístupových klíčů: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Počet hesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Počet přístupových klíčů: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Jiné zařízení"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Další správci hesel"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zavřít list"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zpět na předchozí stránku"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Použít uložený přístupový klíč pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Použít uložené přihlášení pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vyberte uložené přihlášení pro <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Přihlásit se jinak"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, díky"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Pokračovat"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Možnosti přihlašování"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pro uživatele <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Uzamčení správci hesel"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Klepnutím odemknete"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Spravovat přihlášení"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z jiného zařízení"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použít jiné zařízení"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml
new file mode 100644
index 0000000..86cf9ff
--- /dev/null
+++ b/packages/CredentialManager/res/values-da/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Annuller"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Fortsæt"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Opret et andet sted"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Gem et andet sted"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Brug en anden enhed"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Gem på en anden enhed"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"En nemmere og mere sikker måde at logge ind på"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Brug dit fingeraftryk, dit ansigt eller din skærmlås for at logge ind med en unik adgangsnøgle, der hverken kan glemmes eller stjæles. Få flere oplysninger"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Vælg, hvor du vil <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"gem din adgangskode"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"gem dine loginoplysninger"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vil du oprette en adgangsnøgle i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vil du gemme din adgangskode i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vil du gemme dine loginoplysninger i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Du kan bruge din <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> på enhver enhed. Den gemmes i <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"adgangsnøgle"</string>
+    <string name="password" msgid="6738570945182936667">"adgangskode"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"loginmetoder"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vil du bruge <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> til alle dine loginmetoder?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Angiv som standard"</string>
+    <string name="use_once" msgid="9027366575315399714">"Brug én gang"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> adgangskoder, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> adgangsnøgler"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> adgangskoder"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> adgangsnøgler"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"En anden enhed"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Andre adgangskodeadministratorer"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Luk arket"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbage til den forrige side"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruge din gemte adgangsnøgle til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruge din gemte loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vælg en gemt loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Log ind på en anden måde"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nej tak"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Fortsæt"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Valgmuligheder for login"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låste adgangskodeadministratorer"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tryk for at låse op"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrer loginmetoder"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en anden enhed"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Brug en anden enhed"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml
new file mode 100644
index 0000000..d239dd3
--- /dev/null
+++ b/packages/CredentialManager/res/values-de/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Abbrechen"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Weiter"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"An anderem Speicherort erstellen"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"An anderem Ort speichern"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Anderes Gerät verwenden"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Auf einem anderen Gerät speichern"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Einfach und sicher anmelden"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Verwende deinen Fingerabdruck oder deine Gesichts- bzw. Displaysperre, um dich mit einem eindeutigen Passkey anzumelden, der nicht vergessen oder gestohlen werden kann. Weitere Informationen"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Ort auswählen für: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"Passwort speichern"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"Anmeldedaten speichern"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Passkey hier erstellen: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Passwort hier speichern: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Anmeldedaten hier speichern: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Du kannst <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> auf jedem beliebigen Gerät verwenden. Gespeichert (<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>) für <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"Passkey"</string>
+    <string name="password" msgid="6738570945182936667">"Passwort"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"Anmeldungen"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> für alle Anmeldungen verwenden?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Als Standard festlegen"</string>
+    <string name="use_once" msgid="9027366575315399714">"Einmal verwenden"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> Passwörter, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> Passkeys"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> Passwörter"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> Passkeys"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Ein anderes Gerät"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Andere Passwortmanager"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Tabellenblatt schließen"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zurück zur vorherigen Seite"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gespeicherten Passkey für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gespeicherte Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Gespeicherte Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> auswählen"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Andere Anmeldeoption auswählen"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nein danke"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Weiter"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Anmeldeoptionen"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Für <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gesperrte Passwortmanager"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Zum Entsperren tippen"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Anmeldedaten verwalten"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Von einem anderen Gerät"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Anderes Gerät verwenden"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml
new file mode 100644
index 0000000..9b7ccbb
--- /dev/null
+++ b/packages/CredentialManager/res/values-el/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Ακύρωση"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Συνέχεια"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Δημιουργία σε άλλη θέση"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Αποθήκευση σε άλλη θέση"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Χρήση άλλης συσκευής"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Αποθήκευση σε άλλη συσκευή"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Ένας απλός τρόπος για ασφαλή σύνδεση"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Χρησιμοποιήστε το δακτυλικό αποτύπωμα, το πρόσωπο ή το κλείδωμα οθόνης σας για να συνδεθείτε με ένα μοναδικό κλειδί πρόσβασης που δεν είναι δυνατό να ξεχάσετε ή να κλαπεί. Μάθετε περισσότερα"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Επιλέξτε θέση για <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"αποθήκευση του κωδικού πρόσβασής σας"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"αποθήκευση των στοιχείων σύνδεσής σας"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Να δημιουργηθει κλειδί πρόσβασης στο <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>;"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Να αποθηκευτεί ο κωδικός πρόσβασής σας στο <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>;"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Να αποθηκευτούν τα στοιχεία σύνδεσής σας στο <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>;"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Μπορείτε να χρησιμοποιήσετε το στοιχείο τύπου <xliff:g id="TYPE">%2$s</xliff:g> του τομέα <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> σε οποιαδήποτε συσκευή. Αποθηκεύτηκε στο <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> για <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"κλειδί πρόσβασης"</string>
+    <string name="password" msgid="6738570945182936667">"κωδικός πρόσβασης"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"στοιχεία σύνδεσης"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Να χρησιμοποιηθεί το <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> για όλες τις συνδέσεις σας;"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Ορισμός ως προεπιλογής"</string>
+    <string name="use_once" msgid="9027366575315399714">"Χρήση μία φορά"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> κωδικοί πρόσβασης, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> κλειδιά πρόσβασης"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> κωδικοί πρόσβασης"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> κλειδιά πρόσβασης"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Άλλη συσκευή"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Άλλοι διαχειριστές κωδικών πρόσβασης"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Κλείσιμο φύλλου"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Επιστροφή στην προηγούμενη σελίδα"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Να χρησιμοποιηθεί το αποθηκευμένο κλειδί πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Να χρησιμοποιηθούν τα αποθηκευμένα στοιχεία σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Επιλογή αποθηκευμένων στοιχείων σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Σύνδεση με άλλον τρόπο"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Όχι, ευχαριστώ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Συνέχεια"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Επιλογές σύνδεσης"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Κλειδωμένοι διαχειριστές κωδικών πρόσβασης"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Πατήστε για ξεκλείδωμα"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Διαχείριση στοιχείων σύνδεσης"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Από άλλη συσκευή"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Χρήση διαφορετικής συσκευής"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..682dffb
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rAU/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"password"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+    <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..4ec2872
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rCA/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string>
+    <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"password"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+    <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+    <string name="passkey_before_subtitle" msgid="2448119456208647444">"Passkey"</string>
+    <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..682dffb
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rGB/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"password"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+    <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..682dffb
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rIN/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"password"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+    <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..d114a46
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rXC/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="4539824758261855508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎Credential Manager‎‏‎‎‏‎"</string>
+    <string name="string_cancel" msgid="6369133483981306063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎Cancel‎‏‎‎‏‎"</string>
+    <string name="string_continue" msgid="1346732695941131882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎Continue‎‏‎‎‏‎"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎Create in another place‎‏‎‎‏‎"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎Save to another place‎‏‎‎‏‎"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‎Use another device‎‏‎‎‏‎"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎Save to another device‎‏‎‎‏‎"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎A simple way to sign in safely‎‏‎‎‏‎"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more‎‏‎‎‏‎"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎Choose where to ‎‏‎‎‏‏‎<xliff:g id="CREATETYPES">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎save your password‎‏‎‎‏‎"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎save your sign-in info‎‏‎‎‏‎"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎Create a passkey in ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎Save your password to ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎Save your sign-in info to ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎You can use your ‎‏‎‎‏‏‎<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ on any device. It is saved to ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎ for ‎‏‎‎‏‏‎<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="passkey" msgid="632353688396759522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎passkey‎‏‎‎‏‎"</string>
+    <string name="password" msgid="6738570945182936667">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎password‎‏‎‎‏‎"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎sign-ins‎‏‎‎‏‎"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎Use ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ for all your sign-ins?‎‏‎‎‏‎"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‎Set as default‎‏‎‎‏‎"</string>
+    <string name="use_once" msgid="9027366575315399714">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‏‎‎Use once‎‏‎‎‏‎"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ passwords, ‎‏‎‎‏‏‎<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>‎‏‎‎‏‏‏‎ passkeys‎‏‎‎‏‎"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ passwords‎‏‎‎‏‎"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ passkeys‎‏‎‎‏‎"</string>
+    <string name="passkey_before_subtitle" msgid="2448119456208647444">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎Passkey‎‏‎‎‏‎"</string>
+    <string name="another_device" msgid="5147276802037801217">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎Another device‎‏‎‎‏‎"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎Other password managers‎‏‎‎‏‎"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎Close sheet‎‏‎‎‏‎"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎Go back to the previous page‎‏‎‎‏‎"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎Use your saved passkey for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎Use your saved sign-in for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‏‎Choose a saved sign-in for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‎Sign in another way‎‏‎‎‏‎"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎No thanks‎‏‎‎‏‎"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎Continue‎‏‎‎‏‎"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎Sign-in options‎‏‎‎‏‎"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎For ‎‏‎‎‏‏‎<xliff:g id="USERNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎Locked password managers‎‏‎‎‏‎"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎Tap to unlock‎‏‎‎‏‎"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎Manage sign-ins‎‏‎‎‏‎"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎From another device‎‏‎‎‏‎"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎Use a different device‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..96e7697
--- /dev/null
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Crear en otra ubicación"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Guardar en otra ubicación"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usar otro dispositivo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Guardar en otro dispositivo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un modo simple y seguro de ingresar"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa tu huella dactilar, tu rostro o el bloqueo de pantalla para acceder con una llave de acceso única que no olvidarás ni podrán robarte. Más información"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Elige dónde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"guardar tu contraseña"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"guardar tu información de acceso"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"¿Quieres crear una llave de acceso en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"¿Quieres guardar tu contraseña de <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"¿Quieres guardar tu información de acceso a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Puedes usar tu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> en cualquier dispositivo. Se guardó en <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+    <string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
+    <string name="password" msgid="6738570945182936667">"contraseña"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"accesos"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"¿Quieres usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos tus accesos?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Establecer como predeterminado"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usar una vez"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> llaves de acceso, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> contraseñas"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> llaves de acceso"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Otro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Otros administradores de contraseñas"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Quieres usar tu llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Quieres usar tu acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Elige un acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Acceder de otra forma"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, gracias"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opciones de acceso"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Administradores de contraseñas bloqueados"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Presiona para desbloquear"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrar accesos"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Desde otro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otra voz"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml
new file mode 100644
index 0000000..dce1a8e
--- /dev/null
+++ b/packages/CredentialManager/res/values-es/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Crear en otro lugar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Guardar en otro lugar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usar otro dispositivo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Guardar en otro dispositivo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Una forma sencilla y segura de iniciar sesión"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa la huella digital, la cara o el bloqueo de pantalla para iniciar sesión con una llave de acceso única que no se puede olvidar ni robar. Más información"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Elige dónde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"guardar tu contraseña"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"guardar tu información de inicio de sesión"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"¿Crear una llave de acceso en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"¿Guardar tu contraseña en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"¿Guardar tu información de inicio de sesión en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Puedes usar tu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> en cualquier dispositivo. Se guarda en <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+    <string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
+    <string name="password" msgid="6738570945182936667">"contraseña"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"inicios de sesión"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"¿Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos tus inicios de sesión?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Fijar como predeterminado"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usar una vez"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> llaves de acceso"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> llaves de acceso"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Otro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Otros gestores de contraseñas"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Usar la llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Usar el inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Elige un inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Iniciar sesión de otra manera"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, gracias"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opciones de inicio de sesión"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestores de contraseñas bloqueados"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toca para desbloquear"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionar inicios de sesión"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De otro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otro dispositivo"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml
new file mode 100644
index 0000000..00396e0
--- /dev/null
+++ b/packages/CredentialManager/res/values-et/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Tühista"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Jätka"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Loo teises kohas"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvesta teise kohta"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Kasuta teist seadet"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvesta teise seadmesse"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Lihtne viis turvaliselt sisselogimiseks"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Kasutage sõrmejälge, nägu või ekraanilukku, et logida sisse unikaalse pääsuvõtmega, mida ei saa unustada ega varastada. Lisateave"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Valige, kus <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"parool salvestada"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"sisselogimisandmed salvestada"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Kas luua teenuses <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pääsuvõti?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Kas salvestada parool teenusesse <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Kas salvestada sisselogimisteave teenusesse <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Saate rakendust <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> kasutada mis tahes seadmes. Salvestatakse teenusesse <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> – <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"pääsukood"</string>
+    <string name="password" msgid="6738570945182936667">"parool"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"sisselogimisandmed"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Kas kasutada teenust <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kõigi teie sisselogimisandmete puhul?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Määra vaikeseadeks"</string>
+    <string name="use_once" msgid="9027366575315399714">"Kasuta ühe korra"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parooli, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> pääsuvõtit"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parooli"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> pääsuvõtit"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Teine seade"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Muud paroolihaldurid"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Sule leht"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Minge tagasi eelmisele lehele"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Kas kasutada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud pääsuvõtit?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Kas kasutada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud sisselogimisandmeid?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Valige rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud sisselogimisandmed"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Logige sisse muul viisil"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Tänan, ei"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Jätka"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sisselogimise valikud"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Kasutajale <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Lukustatud paroolihaldurid"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Avamiseks puudutage"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Sisselogimisandmete haldamine"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Muus seadmes"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Kasuta teist seadet"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml
new file mode 100644
index 0000000..38f6592
--- /dev/null
+++ b/packages/CredentialManager/res/values-eu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Utzi"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Egin aurrera"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Sortu beste toki batean"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Gorde beste toki batean"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Erabili beste gailu bat"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Gorde beste gailu batean"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Segurtasun osoz saioa hasteko modu erraza"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Erabili hatz-marka, aurpegia edo pantailaren blokeoa ahaztu edo lapurtu ezin den sarbide-gako baten bidez saioa hasteko. Lortu informazio gehiago"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Aukeratu non <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"gorde pasahitza"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"gorde kredentzialei buruzko informazioa"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Sarbide-gako bat sortu nahi duzu <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> aplikazioan?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Pasahitza <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> aplikazioan gorde nahi duzu?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Kredentzialei buruzko informazioa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> aplikazioan gorde nahi duzu?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> aplikazioko <xliff:g id="TYPE">%2$s</xliff:g> edozein gailutan erabil dezakezu. <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> aplikazioan dago gordeta (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)."</string>
+    <string name="passkey" msgid="632353688396759522">"sarbide-gakoa"</string>
+    <string name="password" msgid="6738570945182936667">"pasahitza"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"kredentzialak"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> erabili nahi duzu kredentzial guztietarako?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Ezarri lehenetsi gisa"</string>
+    <string name="use_once" msgid="9027366575315399714">"Erabili behin"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> pasahitz eta <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> sarbide-gako"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> pasahitz"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> sarbide-gako"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Beste gailu bat"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Beste pasahitz-kudeatzaile batzuk"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Itxi orria"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Itzuli aurreko orrira"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde duzun sarbide-gakoa erabili nahi duzu?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde dituzun kredentzialak erabili nahi dituzu?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde dituzun kredentzialak"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Hasi saioa beste modu batean"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ez, eskerrik asko"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Egin aurrera"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Saioa hasteko aukerak"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearenak"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Blokeatutako pasahitz-kudeatzaileak"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Desblokeatzeko, sakatu hau"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kudeatu kredentzialak"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Beste gailu batean gordetakoak"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Erabili beste gailu bat"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml
new file mode 100644
index 0000000..fdfd1e3
--- /dev/null
+++ b/packages/CredentialManager/res/values-fa/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"لغو"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ادامه"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"ایجاد در مکانی دیگر"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"ذخیره در مکانی دیگر"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"استفاده از دستگاهی دیگر"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"ذخیره در دستگاهی دیگر"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"روشی ساده برای ورود به سیستم ایمن"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"برای ورود به سیستم با گذرکلیدی یکتا که غیرقابل فراموش شدن یا دزدیده شدن باشد، از اثر انگشت، چهره، یا قفل صفحه استفاده کنید. بیشتر بدانید"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"انتخاب محل <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ذخیره گذرواژه"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ذخیره اطلاعات ورود به سیستم"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"گذرکلید در <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ایجاد شود؟"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"گذرواژه در <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ذخیره شود؟"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"اطلاعات ورود به سیستم در <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ذخیره شود؟"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"می‌توانید از <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> در هر دستگاهی استفاده کنید. در <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> برای <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ذخیره می‌شود"</string>
+    <string name="passkey" msgid="632353688396759522">"گذرکلید"</string>
+    <string name="password" msgid="6738570945182936667">"گذرواژه"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ورود به سیستم‌ها"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"از <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> برای همه ورود به سیستم‌ها استفاده شود؟"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"تنظیم به‌عنوان پیش‌فرض"</string>
+    <string name="use_once" msgid="9027366575315399714">"یک‌بار استفاده"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> گذرواژه، <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> گذرکلید"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> گذرواژه"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> گذرکلید"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"دستگاهی دیگر"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"دیگر مدیران گذرواژه"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"بستن برگ"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"برگشتن به صفحه قبلی"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"گذرکلید ذخیره‌شده برای <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ورود به سیستم ذخیره‌شده برای <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"انتخاب ورود به سیستم ذخیره‌شده برای <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ورود به سیستم به روشی دیگر"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"نه متشکرم"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ادامه"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"گزینه‌های ورود به سیستم"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"برای <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"مدیران گذرواژه قفل‌شده"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"برای باز کردن قفل ضربه بزنید"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"مدیریت ورود به سیستم‌ها"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"از دستگاهی دیگر"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استفاده از دستگاه دیگری"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml
new file mode 100644
index 0000000..26cfbe5
--- /dev/null
+++ b/packages/CredentialManager/res/values-fi/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Peru"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Jatka"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Luo muualla"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Tallenna muualle"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Käytä toista laitetta"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Helppo tapa kirjautua turvallisesti sisään"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Käytä sormenjälkeä, kasvoja tai näytön lukitusta, niin voit kirjautua sisään yksilöllisellä avainkoodilla, jota ei voi unohtaa tai varastaa. Lue lisää"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Valitse paikka: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"tallenna salasanasi"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"tallenna kirjautumistiedot"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Luodaanko avainkoodi (<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>)?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Tallennetaanko salasanasi tänne: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Tallennetaanko kirjautumistietosi tänne: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> (<xliff:g id="TYPE">%2$s</xliff:g>) on käytettävissä millä tahansa laitteella. Se tallennetaan tänne: <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)"</string>
+    <string name="passkey" msgid="632353688396759522">"avainkoodi"</string>
+    <string name="password" msgid="6738570945182936667">"salasana"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"sisäänkirjautumiset"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Otetaanko <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> käyttöön kaikissa sisäänkirjautumisissa?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Aseta oletukseksi"</string>
+    <string name="use_once" msgid="9027366575315399714">"Käytä kerran"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> salasanaa, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> avainkoodia"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> salasanaa"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> avainkoodia"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Toinen laite"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Muut salasanojen ylläpitotyökalut"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Sulje taulukko"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Takaisin edelliselle sivulle"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Käytetäänkö tallennettua avainkoodiasi täällä: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Käytetäänkö tallennettuja kirjautumistietoja täällä: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Valitse tallennetut kirjautumistiedot (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Kirjaudu sisään toisella tavalla"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ei kiitos"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Jatka"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Kirjautumisvaihtoehdot"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Käyttäjä: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Lukitut salasanojen ylläpitotyökalut"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Avaa napauttamalla"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Muuta kirjautumistietoja"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Toiselta laitteelta"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Käytä toista laitetta"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..ef3d325
--- /dev/null
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Créer à un autre emplacement"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Enregistrer à un autre emplacement"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Utiliser un autre appareil"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Enregistrer sur un autre appareil"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Une manière simple de se connecter en toute sécurité"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Utilisez vos empreintes digitales, votre visage ou un écran de verrouillage pour vous connecter avec une clé d\'accès unique qui ne peut pas être oubliée ou volée. En savoir plus"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Choisir où <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"enregistrer votre mot de passe"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"enregistrer vos données de connexion"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Créer une clé d\'accès dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Enregistrer votre mot de passe dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Enregistrer vos données de connexion dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Vous pouvez utiliser votre <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> sur n\'importe quel appareil. Il est enregistré sur <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pour <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
+    <string name="password" msgid="6738570945182936667">"mot de passe"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Utiliser <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pour toutes vos connexions?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
+    <string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clés d\'accès"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> clés d\'accès"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Un autre appareil"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Autres gestionnaires de mots de passe"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Fermer la feuille"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Retourner à la page précédente"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser votre connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choisir une connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Se connecter d\'une autre manière"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Non merci"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuer"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Options de connexion"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestionnaires de mots de passe verrouillés"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toucher pour déverrouiller"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gérer les connexions"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"À partir d\'un autre appareil"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml
new file mode 100644
index 0000000..f660dde
--- /dev/null
+++ b/packages/CredentialManager/res/values-fr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Créer ailleurs"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Enregistrer ailleurs"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Utiliser un autre appareil"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Enregistrer sur un autre appareil"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Une façon simple et sécurisée de vous connecter"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Utilisez votre empreinte digitale, votre visage ou le verrouillage de l\'écran pour vous connecter avec une clé d\'accès unique que vous ne pourrez pas oublier ni vous faire voler. En savoir plus"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Choisir où <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"enregistrer votre mot de passe"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"enregistrer vos informations de connexion"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Créer une clé d\'accès dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Enregistrer votre mot de passe dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Enregistrer vos informations de connexion dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Vous pouvez utiliser votre <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> sur n\'importe quel appareil. Il est enregistré dans <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pour <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+    <string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
+    <string name="password" msgid="6738570945182936667">"mot de passe"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Utiliser <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pour toutes vos connexions ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
+    <string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clés d\'accès"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> clés d\'accès"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Un autre appareil"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Autres gestionnaires de mots de passe"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Fermer la feuille"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revenir à la page précédente"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser vos informations de connexion enregistrées pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choisir des informations de connexion enregistrées pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Se connecter d\'une autre manière"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Non, merci"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuer"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Options de connexion"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestionnaires de mots de passe verrouillés"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Appuyer pour déverrouiller"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gérer les connexions"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Depuis un autre appareil"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml
new file mode 100644
index 0000000..cacec21
--- /dev/null
+++ b/packages/CredentialManager/res/values-gl/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Crear noutro lugar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Gardar noutro lugar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un xeito fácil de iniciar sesión de forma segura"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa a impresión dixital, a cara ou o bloqueo de pantalla para iniciar sesión cunha clave de acceso única que non podes esquecer nin cha poden roubar. Máis información"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Escolle onde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"gardar o contrasinal"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"gardar a información de inicio de sesión"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Queres crear unha clave de acceso en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Queres gardar o contrasinal en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Queres gardar a información de inicio de sesión en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Podes usar o teu <xliff:g id="TYPE">%2$s</xliff:g> de <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> en calquera dispositivo. Está gardado en <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"clave de acceso"</string>
+    <string name="password" msgid="6738570945182936667">"contrasinal"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"métodos de inicio de sesión"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Queres usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> cada vez que inicies sesión?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Establecer como predeterminado"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usar unha vez"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasinais, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> claves de acceso"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasinais"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> claves de acceso"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Outros xestores de contrasinais"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Pechar folla"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver á páxina anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Queres usar a clave de acceso gardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Queres usar o método de inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolle un método de inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Iniciar sesión doutra forma"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Non, grazas"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcións de inicio de sesión"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Xestores de contrasinais bloqueados"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toca para desbloquear"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Xestionar os métodos de inicio de sesión"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Doutro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar outro dispositivo"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml
new file mode 100644
index 0000000..7ac70aa
--- /dev/null
+++ b/packages/CredentialManager/res/values-gu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"રદ કરો"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ચાલુ રાખો"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"કોઈ અન્ય સ્થાન પર બનાવો"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"કોઈ અન્ય સ્થાન પર સાચવો"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"કોઈ અન્ય ડિવાઇસનો ઉપયોગ કરો"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"અન્ય ડિવાઇસ પર સાચવો"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"સલામત રીતે સાઇન ઇન કરવાની સરળ રીત"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ભૂલી ન શકાય કે ચોરાઈ ન જાય, તેવી કોઈ વિશિષ્ટ પાસકી વડે સાઇન ઇન કરવા માટે, તમારી ફિંગરપ્રિન્ટ, ચહેરો અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો. વધુ જાણો"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ક્યાં સાચવવી છે, તે પસંદ કરો"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"તમારો પાસવર્ડ સાચવો"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"તમારી સાઇન-ઇનની માહિતી સાચવો"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"શું <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>માં કોઈ પાસકી બનાવીએ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"શું તમારો પાસવર્ડ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>માં સાચવીએ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"શું તમારી સાઇન-ઇનની માહિતી <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>માં સાચવીએ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"તમે કોઈપણ ડિવાઇસ પર તમારા <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>નો ઉપયોગ કરી શકો છો. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> માટે <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>માં તેને સાચવવામાં આવે છે"</string>
+    <string name="passkey" msgid="632353688396759522">"પાસકી"</string>
+    <string name="password" msgid="6738570945182936667">"પાસવર્ડ"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"સાઇન-ઇન"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"શું તમારા બધા સાઇન-ઇન માટે <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>નો ઉપયોગ કરીએ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ડિફૉલ્ટ તરીકે સેટ કરો"</string>
+    <string name="use_once" msgid="9027366575315399714">"એકવાર ઉપયોગ કરો"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> પાસવર્ડ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> પાસકી"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> પાસવર્ડ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> પાસકી"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"કોઈ અન્ય ડિવાઇસ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"અન્ય પાસવર્ડ મેનેજર"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"શીટ બંધ કરો"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"પાછલા પેજ પર પરત જાઓ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે શું તમારી સાચવેલી પાસકીનો ઉપયોગ કરીએ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે શું તમારા સાચવેલા સાઇન-ઇનનો ઉપયોગ કરીએ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે કોઈ સાચવેલું સાઇન-ઇન પસંદ કરો"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"કોઈ અન્ય રીતે સાઇન ઇન કરો"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ના, આભાર"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ચાલુ રાખો"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"સાઇન-ઇનના વિકલ્પો"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> માટે"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"લૉક કરેલા પાસવર્ડ મેનેજર"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"અનલૉક કરવા માટે ટૅપ કરો"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"સાઇન-ઇન મેનેજ કરો"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"કોઈ અન્ય ડિવાઇસમાંથી"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"કોઈ અન્ય ડિવાઇસનો ઉપયોગ કરો"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml
new file mode 100644
index 0000000..8d28e0f
--- /dev/null
+++ b/packages/CredentialManager/res/values-hi/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"रद्द करें"</string>
+    <string name="string_continue" msgid="1346732695941131882">"जारी रखें"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"दूसरी जगह पर बनाएं"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"दूसरी जगह पर सेव करें"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"दूसरे डिवाइस का इस्तेमाल करें"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"दूसरे डिवाइस पर सेव करें"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"सुरक्षित तरीके से साइन इन करने का आसान तरीका"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"साइन इन करने के लिए फ़िंगरप्रिंट, फ़ेस या स्क्रीन लॉक जैसी यूनीक पासकी का इस्तेमाल करें. इन्हें, न तो भुलाया जा सकता है न ही चुराया जा सकता है. ज़्यादा जानें"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"चुनें कि <xliff:g id="CREATETYPES">%1$s</xliff:g> कहां पर सेव करना है"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"अपना पासवर्ड सेव करें"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"साइन इन से जुड़ी अपनी जानकारी सेव करें"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"क्या आपको <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> में पासकी बनानी है?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"क्या आपको अपना पासवर्ड <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> में सेव करना है?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"क्या आपको साइन इन करने से जुड़ी जानकारी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> में सेव करनी है?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"अपना <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> किसी भी डिवाइस पर इस्तेमाल किया जा सकता है. इसे <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> के लिए, <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> में सेव किया जाता है"</string>
+    <string name="passkey" msgid="632353688396759522">"पासकी"</string>
+    <string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"साइन इन"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"क्या आपको साइन इन से जुड़ी सारी जानकारी सेव करने के लिए, <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> का इस्तेमाल करना है?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"डिफ़ॉल्ट के तौर पर सेट करें"</string>
+    <string name="use_once" msgid="9027366575315399714">"इसका इस्तेमाल एक बार किया जा सकता है"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड और <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> पासकी"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> पासकी"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"दूसरा डिवाइस"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"दूसरे पासवर्ड मैनेजर"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"शीट बंद करें"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"पिछले पेज पर वापस जाएं"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई पासकी का इस्तेमाल करना है?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई जानकारी का इस्तेमाल करना है?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई जानकारी में से चुनें"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"किसी दूसरे तरीके से साइन इन करें"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"रहने दें"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"जारी रखें"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन इन करने के विकल्प"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> के लिए"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक किए गए पासवर्ड मैनेजर"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"अनलॉक करने के लिए टैप करें"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इन करने की सुविधा को मैनेज करें"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"किसी दूसरे डिवाइस से"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"दूसरे डिवाइस का इस्तेमाल करें"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml
new file mode 100644
index 0000000..06db583
--- /dev/null
+++ b/packages/CredentialManager/res/values-hr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Odustani"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Izradi na drugom mjestu"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Spremi na drugom mjestu"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Upotrijebite neki drugi uređaj"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Spremi na drugi uređaj"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednostavan način za sigurnu prijavu"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Prijavite se otiskom prsta, licem ili zaključavanjem zaslona kao jedinstvenim pristupnim ključem koji je nemoguće zaboraviti ili ukrasti. Saznajte više"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite mjesto za sljedeće: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"spremi zaporku"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"spremi podatke za prijavu"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite li izraditi pristupni ključ na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite li spremiti zaporku na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite li spremiti podatke o prijavi na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Aplikaciju <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> možete upotrijebiti na bilo kojem uređaju. Sprema se na uslugu <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
+    <string name="password" msgid="6738570945182936667">"zaporka"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite li upotrebljavati uslugu <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> za sve prijave?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Postavi kao zadano"</string>
+    <string name="use_once" msgid="9027366575315399714">"Upotrijebi jednom"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Broj zaporki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Broj zaporki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji zaporki"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zatvaranje lista"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite li upotrijebiti spremljeni pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite li upotrijebiti spremljene podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite spremljene podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na neki drugi način"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije prijave"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Upravitelji zaključanih zaporki"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite za otključavanje"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljanje prijavama"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na drugom uređaju"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
new file mode 100644
index 0000000..738de3a
--- /dev/null
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Mégse"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Folytatás"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Létrehozás másik helyen"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Mentés másik helyre"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Másik eszköz használata"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Mentés másik eszközre"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"A biztonságos bejelentkezés egyszerű módja"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Ujjlenyomatát, arcát vagy képernyőzárát használva egyedi azonosítókulccsal jelentkezhet be, amelyet nem lehet elfelejteni vagy ellopni. További információ."</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Válassza ki a(z) <xliff:g id="CREATETYPES">%1$s</xliff:g> helyét"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"jelszó mentése"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"bejelentkezési adatok mentése"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Létrehoz azonosítókulcsot a(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> szolgáltatásban?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Menti jelszavát a(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> szolgáltatásba?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Menti bejelentkezési adatait a(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> szolgáltatásba?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"A(z) <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> bármilyen eszközön használható. A(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> szolgáltatásba van mentve a következő számára: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"azonosítókulcs"</string>
+    <string name="password" msgid="6738570945182936667">"jelszó"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"bejelentkezési adatok"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Szeretné a következőt használni az összes bejelentkezési adatához: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Beállítás alapértelmezettként"</string>
+    <string name="use_once" msgid="9027366575315399714">"Egyszeri használat"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> jelszó, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> azonosítókulcs"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> jelszó"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> azonosítókulcs"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Másik eszköz"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Egyéb jelszókezelők"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Munkalap bezárása"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vissza az előző oldalra"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett azonosítókulcsot használni?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett bejelentkezési adatait használni?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Mentett bejelentkezési adatok választása a következő számára: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Bejelentkezés más módon"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Most nem"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Folytatás"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Bejelentkezési beállítások"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zárolt jelszókezelők"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Koppintson a feloldáshoz"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Bejelentkezési adatok kezelése"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Másik eszközről"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Másik eszköz használata"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
new file mode 100644
index 0000000..7320e64
--- /dev/null
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Չեղարկել"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Շարունակել"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Ստեղծել այլ տեղում"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Պահել այլ տեղում"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Օգտագործել այլ սարք"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Պահել մեկ այլ սարքում"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Մուտք գործելու անվտանգ և պարզ եղանակ"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Օգտագործեք ձեր մատնահետքը, դեմքը կամ էկրանի կողպումը՝ մուտք գործելու հաշիվ եզակի անցաբառի միջոցով, որը հնարավոր չէ կոտրել կամ մոռանալ։ Իմանալ ավելին"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Ընտրեք, թե որտեղ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"պահել գաղտնաբառը"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"պահել մուտքի տվյալները"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Ստեղծե՞լ անցաբառ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածում"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Պահե՞լ ձեր գաղտնաբառը <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածում"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Պահե՞լ ձեր մուտքի տվյալները <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածում"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Դուք կարող եք օգտագործել <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ցանկացած սարքում։ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-ի տվյալները պահված են <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> հավելվածում։"</string>
+    <string name="passkey" msgid="632353688396759522">"անցաբառ"</string>
+    <string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"մուտք"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Միշտ մուտք գործե՞լ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածի միջոցով"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Նշել որպես կանխադրված"</string>
+    <string name="use_once" msgid="9027366575315399714">"Օգտագործել մեկ անգամ"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> գաղտնաբառ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> անցաբառ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> գաղտնաբառ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> անցաբառ"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Այլ սարք"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Գաղտնաբառերի այլ կառավարիչներ"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Փակել թերթը"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Անցնել նախորդ էջ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Օգտագործե՞լ պահված անցաբառը <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Օգտագործե՞լ մուտքի պահված տվյալները <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Ընտրեք մուտքի պահված տվյալներ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Մուտք գործել այլ եղանակով"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ոչ, շնորհակալություն"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Շարունակել"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Մուտքի տարբերակներ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-ի համար"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Գաղտնաբառերի կողպված կառավարիչներ"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Հպեք՝ ապակողպելու համար"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Մուտքի կառավարում"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Մեկ այլ սարքից"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Օգտագործել այլ սարք"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml
new file mode 100644
index 0000000..827a4ff
--- /dev/null
+++ b/packages/CredentialManager/res/values-in/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Batal"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Lanjutkan"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Buat di tempat lain"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Simpan ke tempat lain"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Gunakan perangkat lain"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Simpan ke perangkat lain"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Cara mudah untuk login dengan aman"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gunakan sidik jari, wajah, atau kunci layar untuk login dengan kunci sandi unik yang mudah diingat dan tidak dapat dicuri. Pelajari lebih lanjut"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Pilih tempat untuk <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"menyimpan sandi Anda"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"menyimpan info login Anda"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Buat kunci sandi di <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Simpan sandi ke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Simpan info login ke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Anda dapat menggunakan <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> di perangkat mana pun. Disimpan ke <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> untuk <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"kunci sandi"</string>
+    <string name="password" msgid="6738570945182936667">"sandi"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"login"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gunakan <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> untuk semua info login Anda?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Setel sebagai default"</string>
+    <string name="use_once" msgid="9027366575315399714">"Gunakan sekali"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> sandi, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> kunci sandi"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> sandi"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> kunci sandi"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Perangkat lain"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Pengelola sandi lainnya"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Tutup sheet"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci sandi tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Login dengan cara lain"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Lain kali"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Lanjutkan"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opsi login"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Untuk <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Pengelola sandi terkunci"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Ketuk untuk membuka kunci"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kelola login"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Dari perangkat lain"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan perangkat lain"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml
new file mode 100644
index 0000000..c52e5f7
--- /dev/null
+++ b/packages/CredentialManager/res/values-is/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Hætta við"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Áfram"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Búa til annarsstaðar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Vista annarsstaðar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Nota annað tæki"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Vista í öðru tæki"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Einföld leið við örugga innskráningu"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Notaðu fingrafar, andlit eða skjálás til að skrá þig inn með einkvæmum aðgangslykli sem ekki er hægt að gleyma eða stela. Nánar"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Veldu hvar á að <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"vistaðu aðgangsorðið"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"vistaðu innskráningarupplýsingarnar"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Búa til aðgangslykil í <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vista aðgangsorð í <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vista innskráningarupplýsingar í <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Þú getur notað <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> í hvaða tæki sem er. Það er vistað á <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> fyrir <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"aðgangslykill"</string>
+    <string name="password" msgid="6738570945182936667">"aðgangsorð"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"innskráningar"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Nota <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> fyrir allar innskráningar?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Stilla sem sjálfgefið"</string>
+    <string name="use_once" msgid="9027366575315399714">"Nota einu sinni"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> aðgangsorð, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> aðgangslyklar"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> aðgangsorð"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> aðgangslyklar"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Annað tæki"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Önnur aðgangsorðastjórnun"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Loka blaði"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Fara aftur á fyrri síðu"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Notað vistaðan aðgangslykil fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Nota vistaða innskráningu fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Veldu vistaða innskráningu fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Skrá inn með öðrum hætti"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nei takk"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Áfram"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Innskráningarkostir"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Fyrir: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Læst aðgangsorðastjórnun"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Ýttu til að opna"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Stjórna innskráningu"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Úr öðru tæki"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Nota annað tæki"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml
new file mode 100644
index 0000000..a06135e
--- /dev/null
+++ b/packages/CredentialManager/res/values-it/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Annulla"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continua"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Crea in un altro luogo"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Salva in un altro luogo"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usa un altro dispositivo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Salva su un altro dispositivo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un modo semplice per accedere in sicurezza"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa l\'impronta digitale, il volto o il blocco schermo per accedere con una passkey unica che non può essere dimenticata o rubata. Scopri di più"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Scegli dove <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"salva la password"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"salva le tue informazioni di accesso"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vuoi creare una passkey su <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vuoi salvare la password su <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vuoi salvare le informazioni di accesso su <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Puoi utilizzare <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> su qualsiasi dispositivo. Questa informazione è salvata su <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> per <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"password"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"accessi"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vuoi usare <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> per tutti gli accessi?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Imposta come valore predefinito"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usa una volta"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> password, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkey"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> password"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkey"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Un altro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Altri gestori delle password"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Chiudi il foglio"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Torna alla pagina precedente"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vuoi usare la passkey salvata per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vuoi usare l\'accesso salvato per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Scegli un accesso salvato per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Accedi in un altro modo"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, grazie"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continua"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opzioni di accesso"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Per <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestori delle password bloccati"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tocca per sbloccare"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestisci gli accessi"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Da un altro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usa un dispositivo diverso"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml
new file mode 100644
index 0000000..e9c6adb7
--- /dev/null
+++ b/packages/CredentialManager/res/values-iw/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ביטול"</string>
+    <string name="string_continue" msgid="1346732695941131882">"המשך"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"יצירה במקום אחר"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"שמירה במקום אחר"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"שימוש במכשיר אחר"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"שמירה במכשיר אחר"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"דרך פשוטה להיכנס לחשבון בבטחה"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"אפשר להשתמש בטביעת אצבע, בזיהוי פנים או בנעילת מסך כדי להיכנס לחשבון עם מפתח גישה ייחודי שאי אפשר לשכוח או לגנוב אותו. מידע נוסף"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"צריך לבחור לאן <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"שמירת הסיסמה שלך"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"שמירת פרטי הכניסה שלך"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"ליצור מפתח גישה ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"לשמור את הסיסמה ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"לשמור את פרטי הכניסה ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"אפשר להשתמש ב-<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> מסוג <xliff:g id="TYPE">%2$s</xliff:g> בכל מכשיר. הוא שמור ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ל-<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"מפתח גישה"</string>
+    <string name="password" msgid="6738570945182936667">"סיסמה"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"פרטי כניסה"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"להשתמש ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> בכל הכניסות?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"הגדרה כברירת מחדל"</string>
+    <string name="use_once" msgid="9027366575315399714">"שימוש פעם אחת"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> סיסמאות, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> מפתחות גישה"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> סיסמאות"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> מפתחות גישה"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"מכשיר אחר"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"מנהלי סיסמאות אחרים"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"סגירת הגיליון"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"חזרה לדף הקודם"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"להשתמש במפתח גישה שנשמר עבור <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"להשתמש בפרטי הכניסה שנשמרו עבור <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"בחירת פרטי כניסה שמורים עבור <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"כניסה בדרך אחרת"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"לא תודה"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"המשך"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"אפשרויות כניסה לחשבון"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"עבור <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"מנהלי סיסמאות נעולים"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"יש להקיש כדי לבטל את הנעילה"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ניהול כניסות"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ממכשיר אחר"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"צריך להשתמש במכשיר אחר"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
new file mode 100644
index 0000000..8e448eb
--- /dev/null
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"キャンセル"</string>
+    <string name="string_continue" msgid="1346732695941131882">"続行"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"別の場所で作成"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"別の場所に保存"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"別のデバイスを使用"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"他のデバイスに保存"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"安全にログインする簡単な方法"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"忘れたり盗まれたりする可能性がある一意のパスキーと合わせて、ログインに指紋認証、顔認証、画面ロックを使用できます。詳細"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> の保存場所の選択"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"パスワードを保存"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ログイン情報を保存"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> でパスキーを作成しますか?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> にパスワードを保存しますか?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> にログイン情報を保存しますか?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> の <xliff:g id="TYPE">%2$s</xliff:g> はどのデバイスでも使用できます。<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> の <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> に保存されます"</string>
+    <string name="passkey" msgid="632353688396759522">"パスキー"</string>
+    <string name="password" msgid="6738570945182936667">"パスワード"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ログイン"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ログインのたびに <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> を使用しますか?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"デフォルトに設定"</string>
+    <string name="use_once" msgid="9027366575315399714">"1 回使用"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード、<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 件のパスキー"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 件のパスキー"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"別のデバイス"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"他のパスワード マネージャー"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"シートを閉じます"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"前のページに戻ります"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したパスキーを使用しますか?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報を使用しますか?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報の選択"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"別の方法でログイン"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"利用しない"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"続行"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ログイン オプション"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 用"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"パスワード マネージャー ロック中"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"タップしてロック解除"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ログインを管理"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"別のデバイスを使う"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"別のデバイスを使用"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
new file mode 100644
index 0000000..853ea19
--- /dev/null
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"გაუქმება"</string>
+    <string name="string_continue" msgid="1346732695941131882">"გაგრძელება"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"სხვა სივრცეში შექმნა"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"სხვა სივრცეში შენახვა"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"სხვა მოწყობილობის გამოყენება"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"სხვა მოწყობილობაზე შენახვა"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"უსაფრთხოდ შესვლის მარტივი გზა"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"გამოიყენეთ თქვენი თითის ანაბეჭდი, სახის ამოცნობა და ეკრანის დაბლოკვა სისტემაში უნიკალური წვდომის გასაღებით შესასვლელად, რომლის დავიწყება ან მოპარვა შეუძლებელია. შეიტყვეთ მეტი"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"აირჩიეთ, სად უნდა <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"შეინახეთ თქვენი პაროლი"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"შეინახეთ თქვენი სისტემაში შესვლის ინფორმაცია"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"გსურთ წვდომის გასაღების შექმნა <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-ში?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"გსურთ თქვენი პაროლის შენახვა <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-ში?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"გსურთ თქვენი სისტემაში შესვლის მონაცემების შენახვა <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-ში?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"შეგიძლიათ გამოიყენოთ თქვენი <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ნებისმიერ მოწყობილობაზე. ის შეინახება <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>-ზე <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-თვის"</string>
+    <string name="passkey" msgid="632353688396759522">"წვდომის გასაღები"</string>
+    <string name="password" msgid="6738570945182936667">"პაროლი"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"სისტემაში შესვლა"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"გსურთ, გამოიყენოთ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> სისტემაში ყველა შესვლისთვის?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ნაგულისხმევად დაყენება"</string>
+    <string name="use_once" msgid="9027366575315399714">"ერთხელ გამოყენება"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> პაროლი, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> წვდომის გასაღები"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> პაროლი"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> წვდომის გასაღები"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"სხვა მოწყობილობა"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"პაროლების სხვა მმართველები"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ფურცლის დახურვა"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"წინა გვერდზე დაბრუნება"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"გსურთ თქვენი დამახსოვრებული წვდომის გასაღების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"გსურთ თქვენი დამახსოვრებული სისტემაში შესვლის მონაცემების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"აირჩიეთ სისტემაში შესვლის ინფორმაცია აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"სხვა ხერხით შესვლა"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"არა, გმადლობთ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"გაგრძელება"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"სისტემაში შესვლის ვარიანტები"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ჩაკეტილი პაროლის მმართველები"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"შეეხეთ განსაბლოკად"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"სისტემაში შესვლის მონაცემების მართვა"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"სხვა მოწყობილობიდან"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"გამოიყენეთ სხვა მოწყობილობა"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
new file mode 100644
index 0000000..2271533
--- /dev/null
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Бас тарту"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Жалғастыру"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Басқа орында жасау"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Басқа орынға сақтау"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Басқа құрылғыны пайдалану"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Қауіпсіз кірудің оңай жолы"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Ұмытылмайтын немесе ұрланбайтын бірегей кіру кілтінің көмегімен кіру үшін саусақ ізін, бетті анықтау функциясын немесе экран құлпын пайдаланыңыз. Толық ақпарат"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> таңдау"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"құпия сөзді сақтау"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"тіркелу деректерін сақтау"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> қолданбасында кіру кілті жасалсын ба?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> қолданбасына құпия сөз сақталсын ба?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> қолданбасына тіркелу деректері сақталсын ба?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> кез келген құрылғыда пайдаланылады. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> тіркелу деректері <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> қолданбасында сақталады."</string>
+    <string name="passkey" msgid="632353688396759522">"кіру кілті"</string>
+    <string name="password" msgid="6738570945182936667">"құпия сөз"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"кіру әрекеттері"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Барлық кіру әрекеті үшін <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> пайдаланылсын ба?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Әдепкі етіп орнату"</string>
+    <string name="use_once" msgid="9027366575315399714">"Бір рет пайдалану"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> құпия сөз, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> кіру кілті"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> құпия сөз"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> кіру кілті"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Басқа құрылғы"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Басқа құпия сөз менеджерлері"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Парақты жабу"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Алдыңғы бетке оралу"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған кіру кілті пайдаланылсын ба?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған тіркелу деректері пайдаланылсын ба?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған тіркелу деректерін таңдаңыз"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Басқаша кіру"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Жоқ, рақмет"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Жалғастыру"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Кіру опциялары"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> үшін"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Құлыпталған құпия сөз менеджерлері"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Құлыпты ашу үшін түртіңіз."</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіру әрекеттерін басқару"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml
new file mode 100644
index 0000000..d517810
--- /dev/null
+++ b/packages/CredentialManager/res/values-km/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"បោះបង់"</string>
+    <string name="string_continue" msgid="1346732695941131882">"បន្ត"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"បង្កើតនៅកន្លែងផ្សេងទៀត"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"រក្សាទុកក្នុងកន្លែងផ្សេងទៀត"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"ប្រើ​ឧបករណ៍​ផ្សេងទៀត"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"រក្សាទុកទៅក្នុងឧបករណ៍ផ្សេង"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"វិធីដ៏សាមញ្ញ ដើម្បីចូលគណនីដោយសុវត្ថិភាព"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ប្រើស្នាមម្រាមដៃ មុខ ឬការចាក់សោអេក្រង់របស់អ្នក ដើម្បីចូលគណនីដោយប្រើកូដសម្ងាត់ខុសប្លែកពីគេដែលមិនអាចភ្លេច ឬត្រូវគេលួច។ ស្វែងយល់បន្ថែម"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"ជ្រើសរើសកន្លែងដែលត្រូវ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"រក្សាទុកពាក្យសម្ងាត់របស់អ្នក"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"រក្សាទុកព័ត៌មានចូលគណនីរបស់អ្នក"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"បង្កើតកូដសម្ងាត់នៅក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ឬ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"រក្សាទុកពាក្យសម្ងាត់របស់អ្នកក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ឬ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"រក្សាទុកព័ត៌មានចូលគណនីរបស់អ្នកក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ឬ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"អ្នកអាចប្រើ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> របស់អ្នកនៅលើឧបករណ៍ណាក៏បាន។ វាត្រូវបានរក្សាទុកក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> សម្រាប់ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"កូដសម្ងាត់"</string>
+    <string name="password" msgid="6738570945182936667">"ពាក្យសម្ងាត់"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ការចូល​គណនី"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ប្រើ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> សម្រាប់ការចូលគណនីទាំងអស់របស់អ្នកឬ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"កំណត់ជាលំនាំដើម"</string>
+    <string name="use_once" msgid="9027366575315399714">"ប្រើម្ដង"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"ពាក្យសម្ងាត់ <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> កូដសម្ងាត់ <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"ពាក្យសម្ងាត់ <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"កូដសម្ងាត់ <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"ឧបករណ៍​ផ្សេងទៀត"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ផ្សេងទៀត"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"បិទសន្លឹក"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ត្រឡប់ទៅ​ទំព័រ​មុនវិញ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ប្រើកូដសម្ងាត់ដែលបានរក្សាទុករបស់អ្នកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ប្រើការចូល​គណនីដែលបានរក្សាទុករបស់អ្នកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"ជ្រើសរើសការចូលគណនីដែលបានរក្សាទុកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ចូលគណនីដោយប្រើវិធីផ្សេងទៀត"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ទេ អរគុណ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"បន្ត"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ជម្រើស​ចូលគណនី"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"សម្រាប់ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ដែលបានចាក់សោ"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ចុចដើម្បីដោះសោ"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"គ្រប់គ្រងការចូល​គណនី"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ពីឧបករណ៍ផ្សេងទៀត"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ប្រើឧបករណ៍ផ្សេង"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
new file mode 100644
index 0000000..763f8ee
--- /dev/null
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ರದ್ದುಗೊಳಿಸಿ"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ಮುಂದುವರಿಸಿ"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"ಮತ್ತೊಂದು ಸ್ಥಳದಲ್ಲಿ ರಚಿಸಿ"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"ಮತ್ತೊಂದು ಸ್ಥಳದಲ್ಲಿ ಉಳಿಸಿ"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"ಬೇರೊಂದು ಸಾಧನವನ್ನು ಬಳಸಿ"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"ಬೇರೊಂದು ಸಾಧನದಲ್ಲಿ ಉಳಿಸಿ"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"ಸುರಕ್ಷಿತವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡುವ ಸುಲಭ ವಿಧಾನ"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ಮರೆಯಲಾಗದ ಅಥವಾ ಕದಿಯಲಾಗದ ಅನನ್ಯ ಪಾಸ್‌ಕೀ ಮೂಲಕ ಸೈನ್ ಇನ್ ಮಾಡಲು ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್, ಫೇಸ್ ಲಾಕ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ. ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ಅನ್ನು ಎಲ್ಲಿ ಉಳಿಸಬೇಕು ಎಂದು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್‌ ಉಳಿಸಿ"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ನಿಮ್ಮ ಸೈನ್-ಇನ್ ಮಾಹಿತಿ ಉಳಿಸಿ"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ನಲ್ಲಿ ಪಾಸ್‌ಕೀ ರಚಿಸಬೇಕೆ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಗೆ ಉಳಿಸಬೇಕೆ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ನಿಮ್ಮ ಸೈನ್ ಇನ್ ಮಾಹಿತಿಯನ್ನು <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಗೆ ಉಳಿಸಬೇಕೆ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"ನೀವು ಯಾವುದೇ ಸಾಧನದಲ್ಲಿ ನಿಮ್ಮ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ಅನ್ನು ಬಳಸಬಹುದು. ಇದನ್ನು <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ಗಾಗಿ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ಗೆ ಉಳಿಸಲಾಗಿದೆ"</string>
+    <string name="passkey" msgid="632353688396759522">"ಪಾಸ್‌ಕೀ"</string>
+    <string name="password" msgid="6738570945182936667">"ಪಾಸ್‌ವರ್ಡ್"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ಸೈನ್-ಇನ್‌ಗಳು"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ನಿಮ್ಮ ಎಲ್ಲಾ ಸೈನ್-ಇನ್‌ಗಳಿಗಾಗಿ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸುವುದೇ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ಡೀಫಾಲ್ಟ್ ಆಗಿ ಸೆಟ್ ಮಾಡಿ"</string>
+    <string name="use_once" msgid="9027366575315399714">"ಒಂದು ಬಾರಿ ಬಳಸಿ"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ಪಾಸ್‌ಕೀಗಳು"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ಪಾಸ್‌ವರ್ಡ್‌ಗಳು"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ಪಾಸ್‌ಕೀಗಳು"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"ಮತ್ತೊಂದು ಸಾಧನ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"ಇತರ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕರು"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ಶೀಟ್ ಮುಚ್ಚಿರಿ"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ಹಿಂದಿನ ಪುಟಕ್ಕೆ ಹಿಂದಿರುಗಿ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ನಿಮ್ಮ ಪಾಸ್‌ಕೀ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ನಿಮ್ಮ ಸೈನ್-ಇನ್ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ಬೇಡ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ಮುಂದುವರಿಸಿ"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ಸೈನ್ ಇನ್ ಆಯ್ಕೆಗಳು"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> ಗಾಗಿ"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕರನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ಸೈನ್-ಇನ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ಮತ್ತೊಂದು ಸಾಧನದಿಂದ"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ಬೇರೆ ಸಾಧನವನ್ನು ಬಳಸಿ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml
new file mode 100644
index 0000000..246790d
--- /dev/null
+++ b/packages/CredentialManager/res/values-ko/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"취소"</string>
+    <string name="string_continue" msgid="1346732695941131882">"계속"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"다른 위치에 만들기"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"다른 위치에 저장"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"다른 기기 사용"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"다른 기기에 저장"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"안전하게 로그인하는 간단한 방법"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"지문, 얼굴 인식 또는 화면 잠금을 통해 잊어버리거나 분실할 염려가 없는 고유한 패스키로 로그인하세요. 자세히 알아보기"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> 작업을 위한 위치 선택"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"비밀번호 저장"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"로그인 정보 저장"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>에 패스키를 만드시겠습니까?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>에 비밀번호를 저장하시겠습니까?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>에 로그인 정보를 저장하시겠습니까?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"기기에서 <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>을(를) 사용할 수 있습니다. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>을(를) 위해 <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>에 저장되어 있습니다."</string>
+    <string name="passkey" msgid="632353688396759522">"패스키"</string>
+    <string name="password" msgid="6738570945182936667">"비밀번호"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"로그인 정보"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"모든 로그인에 <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"기본값으로 설정"</string>
+    <string name="use_once" msgid="9027366575315399714">"한 번 사용"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"비밀번호 <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>개, 패스키 <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>개"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"비밀번호 <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>개"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"패스키 <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>개"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"다른 기기"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"기타 비밀번호 관리자"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"시트 닫기"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"이전 페이지로 돌아가기"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 패스키를 사용하시겠습니까?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보를 사용하시겠습니까?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보 선택"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"다른 방법으로 로그인"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"아니요"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"계속"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"로그인 옵션"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>님의 로그인 정보"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"잠긴 비밀번호 관리자"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"잠금 해제하려면 탭하세요."</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"로그인 관리"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"다른 기기에서"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"다른 기기 사용"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
new file mode 100644
index 0000000..3dc7dea
--- /dev/null
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Жок"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Улантуу"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Башка жерде түзүү"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Башка жерге сактоо"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Башка түзмөк колдонуу"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Коопсуз кирүүнүн жөнөкөй жолу"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Унутуп калууга же уурдатууга мүмкүн эмес болгон уникалдуу ачкыч менен манжа изин, жүзүнөн таанып ачуу же экранды кулпулоо функцияларын колдонуп өзүңүздү ырастай аласыз. Кененирээк"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> үчүн жер тандаңыз"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"сырсөзүңүздү сактаңыз"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"кирүү маалыматын сактаңыз"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> колдонмосунда мүмкүндүк алуу ачкычын түзөсүзбү?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Сырсөзүңүздү <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> колдонмосунда сактайсызбы?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Кирүү маалыматын <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> колдонмосунда сактайсызбы?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> каалаган түзмөктө колдонулат. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> байланыштуу маалыматтар <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> колдонмосунда сакталат"</string>
+    <string name="passkey" msgid="632353688396759522">"мүмкүндүк алуу ачкычы"</string>
+    <string name="password" msgid="6738570945182936667">"сырсөз"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"кирүүлөр"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> бардык аккаунттарга кирүү үчүн колдонулсунбу?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Демейки катары коюу"</string>
+    <string name="use_once" msgid="9027366575315399714">"Бир жолу колдонуу"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> сырсөз, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> мүмкүндүк алуу ачкычы"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> сырсөз"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> мүмкүндүк алуу ачкычы"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Башка түзмөк"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Башка сырсөздөрдү башкаргычтар"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Баракты жабуу"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Мурунку бетке кайтуу"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган мүмкүндүк алуу ачкычын колдоносузбу?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган кирүү параметрин колдоносузбу?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү маалыматын тандаңыз"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Башка жол менен кирүү"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Жок, рахмат"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Улантуу"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Аккаунтка кирүү параметрлери"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> үчүн"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Кулпуланган сырсөздөрдү башкаргычтар"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Кулпусун ачуу үчүн таптаңыз"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кирүү параметрлерин тескөө"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Башка түзмөктөн"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Башка түзмөктү колдонуу"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml
new file mode 100644
index 0000000..8efc8a8
--- /dev/null
+++ b/packages/CredentialManager/res/values-lo/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ຍົກເລີກ"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ສືບຕໍ່"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"ສ້າງໃນບ່ອນອື່ນ"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"ບັນທຶກໃສ່ບ່ອນອື່ນ"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"ໃຊ້ອຸປະກອນອື່ນ"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"ບັນທຶກໃສ່ອຸປະກອນອື່ນ"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"ວິທີງ່າຍໆໃນການເຂົ້າສູ່ລະບົບຢ່າງປອດໄພ"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ໃຊ້ລາຍນິ້ວມື, ໃບໜ້າ ຫຼື ລັອກໜ້າຈໍຂອງທ່ານເພື່ອເຂົ້າສູ່ລະບົບດ້ວຍກະແຈຜ່ານທີ່ບໍ່ຊ້ຳກັນເພື່ອບໍ່ໃຫ້ລືມ ຫຼື ຖືກລັກໄດ້. ສຶກສາເພີ່ມເຕີມ"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"ເລືອກບ່ອນທີ່ຈະ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ບັນທຶກລະຫັດຜ່ານຂອງທ່ານ"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບຂອງທ່ານ"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"ສ້າງກະແຈຜ່ານໃນ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ບໍ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"ບັນທຶກລະຫັດຜ່ານຂອງທ່ານໃສ່ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ບໍ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບຂອງທ່ານໃສ່ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ບໍ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"ທ່ານສາມາດໃຊ້ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ຂອງທ່ານຢູ່ອຸປະກອນໃດກໍໄດ້. ມັນຈະຖືກບັນທຶກໃສ່ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ສຳລັບ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"ກະແຈຜ່ານ"</string>
+    <string name="password" msgid="6738570945182936667">"ລະຫັດຜ່ານ"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ການເຂົ້າສູ່ລະບົບ"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ໃຊ້ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ສຳລັບການເຂົ້າສູ່ລະບົບທັງໝົດຂອງທ່ານບໍ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ຕັ້ງເປັນຄ່າເລີ່ມຕົ້ນ"</string>
+    <string name="use_once" msgid="9027366575315399714">"ໃຊ້ເທື່ອດຽວ"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ລະຫັດຜ່ານ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ກະແຈຜ່ານ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ລະຫັດຜ່ານ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ກະແຈຜ່ານ"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"ອຸປະກອນອື່ນ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"ຕົວຈັດການລະຫັດຜ່ານອື່ນໆ"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ປິດຊີດ"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ກັບຄືນໄປຫາໜ້າກ່ອນໜ້ານີ້"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ໃຊ້ກະແຈຜ່ານທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ໃຊ້ການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"ເລືອກການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ເຂົ້າສູ່ລະບົບດ້ວຍວິທີອື່ນ"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ບໍ່, ຂອບໃຈ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ສືບຕໍ່"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ຕົວເລືອກການເຂົ້າສູ່ລະບົບ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"ສຳລັບ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ຕົວຈັດການລະຫັດຜ່ານທີ່ລັອກໄວ້"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ແຕະເພື່ອປົດລັອກ"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ຈັດການການເຂົ້າສູ່ລະບົບ"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ຈາກອຸປະກອນອື່ນ"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ໃຊ້ອຸປະກອນອື່ນ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml
new file mode 100644
index 0000000..02d3783
--- /dev/null
+++ b/packages/CredentialManager/res/values-lt/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Atšaukti"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Tęsti"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Sukurti kitoje vietoje"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Išsaugoti kitoje vietoje"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Naudoti kitą įrenginį"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Išsaugoti kitame įrenginyje"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Paprastas saugaus prisijungimo metodas"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Naudodami piršto atspaudą, veidą ar ekrano užraktą prisijunkite su unikaliu „passkey“, kurio neįmanoma pamiršti ar pavogti. Sužinokite daugiau"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Pasirinkite, kur <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"išsaugoti slaptažodį"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"išsaugoti prisijungimo informaciją"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Sukurti „passkey“ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Išsaugoti slaptažodį <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Išsaugoti prisijungimo informaciją <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Galite naudoti <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> bet kuriame įrenginyje. Jis išsaugomas <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)"</string>
+    <string name="passkey" msgid="632353688396759522">"„passkey“"</string>
+    <string name="password" msgid="6738570945182936667">"slaptažodis"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"prisijungimo informacija"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Naudoti <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> visada prisijungiant?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Nustatyti kaip numatytąjį"</string>
+    <string name="use_once" msgid="9027366575315399714">"Naudoti vieną kartą"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"slaptažodžių: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, „passkey“: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"slaptažodžių: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"„passkey“: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Kitas įrenginys"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Kitos slaptažodžių tvarkyklės"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Uždaryti lapą"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Grįžti į ankstesnį puslapį"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Naudoti išsaugotą „passkey“ programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Naudoti išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pasirinkite išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prisijungti kitu būdu"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, ačiū"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Tęsti"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Prisijungimo parinktys"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Skirta <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Užrakintos slaptažodžių tvarkyklės"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Palieskite, kad atrakintumėte"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Tvarkyti prisijungimo informaciją"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Naudojant kitą įrenginį"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Naudoti kitą įrenginį"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml
new file mode 100644
index 0000000..cbea91a
--- /dev/null
+++ b/packages/CredentialManager/res/values-lv/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Atcelt"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Turpināt"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Izveidot citur"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Saglabāt citur"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Izmantot citu ierīci"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Vienkāršs veids, kā droši pierakstīties"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Izmantojiet pirksta nospiedumu, autorizāciju pēc sejas vai ekrāna bloķēšanu, lai pierakstītos ar unikālu piekļuves atslēgu, ko nevar aizmirst vai nozagt. Uzziniet vairāk."</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Izvēlieties, kur: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"saglabāt paroli"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"saglabāt pierakstīšanās informāciju"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vai izveidot piekļuves atslēgu šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vai saglabāt paroli šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vai saglabāt pierakstīšanās informāciju šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Jebkurā ierīcē varat izmantot šo: <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>. Tas tiek saglabāt šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>; mērķis: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+    <string name="passkey" msgid="632353688396759522">"piekļuves atslēga"</string>
+    <string name="password" msgid="6738570945182936667">"parole"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"pierakstīšanās informācija"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vai vienmēr izmantot <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>, lai pierakstītos?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Iestatīt kā noklusējumu"</string>
+    <string name="use_once" msgid="9027366575315399714">"Izmantot vienreiz"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> paroles, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> piekļuves atslēgas"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> paroles"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> piekļuves atslēgas"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Cita ierīce"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Citi paroļu pārvaldnieki"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Aizvērt lapu"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Atgriezties iepriekšējā lapā"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vai izmantot saglabāto piekļuves atslēgu lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vai izmantot saglabāto pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Saglabātas pierakstīšanās informācijas izvēle lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Pierakstīties citā veidā"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nē, paldies"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Turpināt"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Pierakstīšanās opcijas"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Lietotājam <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Paroļu pārvaldnieki, kuros nepieciešams autentificēties"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Pieskarieties, lai atbloķētu"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Pierakstīšanās informācijas pārvaldība"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"No citas ierīces"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Izmantot citu ierīci"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml
new file mode 100644
index 0000000..e98bfc4
--- /dev/null
+++ b/packages/CredentialManager/res/values-mk/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Продолжи"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Создајте на друго место"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Зачувајте на друго место"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Употребете друг уред"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Зачувајте на друг уред"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Едноставен начин за безбедно најавување"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Користете го отпечатокот, заклучувањето со лик или заклучувањето екран за да се најавите со единствен криптографски клуч што не може да се заборави или украде. Дознајте повеќе"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Изберете каде да <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"се зачува лозинката"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"се зачуваат податоците за најавување"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Да се создаде криптографски клуч во <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Да се зачува вашата лозинка во <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Да се зачуваат вашите податоци за најавување во <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Вашиот <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> од типот <xliff:g id="TYPE">%2$s</xliff:g> може да го користите на секој уред. Зачуван е на <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"криптографски клуч"</string>
+    <string name="password" msgid="6738570945182936667">"лозинка"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"најавувања"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Да се користи <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> за сите ваши најавувања?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Постави како стандардна опција"</string>
+    <string name="use_once" msgid="9027366575315399714">"Употребете еднаш"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> лозинки, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> криптографски клучеви"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> лозинки"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> криптографски клучеви"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Друг уред"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Други управници со лозинки"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Затворете го листот"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Врати се на претходната страница"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се користи вашиот зачуван криптографски клуч за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се користи вашето зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Изберете зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Најавете се на друг начин"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, фала"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Продолжи"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опции за најавување"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заклучени управници со лозинки"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Допрете за да отклучите"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управувајте со најавувањата"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Од друг уред"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Употребете друг уред"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml
new file mode 100644
index 0000000..34029ce
--- /dev/null
+++ b/packages/CredentialManager/res/values-ml/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"റദ്ദാക്കുക"</string>
+    <string name="string_continue" msgid="1346732695941131882">"തുടരുക"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"മറ്റൊരു സ്ഥലത്ത് സൃഷ്‌ടിക്കുക"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"മറ്റൊരു സ്ഥലത്തേക്ക് സംരക്ഷിക്കുക"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"മറ്റൊരു ഉപകരണം ഉപയോഗിക്കുക"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"മറ്റൊരു ഉപകരണത്തിലേക്ക് സംരക്ഷിക്കുക"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"സുരക്ഷിതമായി സൈൻ ഇൻ ചെയ്യാനുള്ള ലളിതമായ മാർഗ്ഗം"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"മറന്നുപോകാനും മോഷ്‌ടിക്കാനും സാധ്യതയില്ലാത്ത തനത് പാസ്‌കീ ഉപയോഗിച്ച് സൈൻ ഇൻ ചെയ്യാൻ നിങ്ങളുടെ ഫിംഗർപ്രിന്റോ മുഖമോ സ്‌ക്രീൻ ലോക്കോ ഉപയോഗിക്കുക. കൂടുതലറിയുക"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"എവിടെ <xliff:g id="CREATETYPES">%1$s</xliff:g> എന്ന് തിരഞ്ഞെടുക്കുക"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"നിങ്ങളുടെ പാസ്‌വേഡ് സംരക്ഷിക്കുക"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"നിങ്ങളുടെ സൈൻ ഇൻ വിവരങ്ങൾ സംരക്ഷിക്കുക"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> എന്നതിൽ ഒരു പാസ്‌കീ സൃഷ്‌ടിക്കണോ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"പാസ്‌വേഡ് <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"സൈൻ ഇൻ വിവരങ്ങൾ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"നിങ്ങളുടെ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ഏത് ഉപകരണത്തിലും നിങ്ങൾക്ക് ഉപയോഗിക്കാം. ഇത് <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> എന്നതിനായി <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> എന്നതിലേക്ക് സംരക്ഷിച്ചു"</string>
+    <string name="passkey" msgid="632353688396759522">"പാസ്‌കീ"</string>
+    <string name="password" msgid="6738570945182936667">"പാസ്‌വേഡ്"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"സൈൻ ഇന്നുകൾ"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"നിങ്ങളുടെ എല്ലാ സൈൻ ഇന്നുകൾക്കും <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ഉപയോഗിക്കണോ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ഡിഫോൾട്ടായി സജ്ജീകരിക്കുക"</string>
+    <string name="use_once" msgid="9027366575315399714">"ഒരു തവണ ഉപയോഗിക്കുക"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> പാസ്‌വേഡുകൾ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> പാസ്‌കീകൾ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> പാസ്‌വേഡുകൾ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> പാസ്‌കീകൾ"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"മറ്റൊരു ഉപകരണം"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"മറ്റ് പാസ്‌വേഡ് മാനേജർമാർ"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ഷീറ്റ് അടയ്ക്കുക"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"മുമ്പത്തെ പേജിലേക്ക് മടങ്ങുക"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി നിങ്ങൾ സംരക്ഷിച്ച പാസ്‌കീ ഉപയോഗിക്കണോ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി നിങ്ങൾ സംരക്ഷിച്ച സൈൻ ഇൻ ഉപയോഗിക്കണോ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി ഒരു സംരക്ഷിച്ച സൈൻ ഇൻ തിരഞ്ഞെടുക്കുക"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"മറ്റൊരു രീതിയിൽ സൈൻ ഇൻ ചെയ്യുക"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"വേണ്ട"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"തുടരുക"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"സൈൻ ഇൻ ഓപ്ഷനുകൾ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്നയാൾക്ക്"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ലോക്ക് ചെയ്‌ത പാസ്‌വേഡ് സൈൻ ഇൻ മാനേജർമാർ"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"അൺലോക്ക് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"സൈൻ ഇന്നുകൾ മാനേജ് ചെയ്യുക"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"മറ്റൊരു ഉപകരണത്തിൽ നിന്ന്"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"മറ്റൊരു ഉപകരണം ഉപയോഗിക്കുക"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml
new file mode 100644
index 0000000..f090931
--- /dev/null
+++ b/packages/CredentialManager/res/values-mn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Цуцлах"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Үргэлжлүүлэх"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Өөр газар үүсгэх"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Өөр газар хадгалах"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Өөр төхөөрөмж ашиглах"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Өөр төхөөрөмжид хадгалах"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Аюулгүй нэвтрэх энгийн арга"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Мартах эсвэл хулгайд алдах боломжгүй өвөрмөц passkey-н хамт нэвтрэх хурууны хээ, царай эсвэл дэлгэцийн түгжээгээ ашиглана уу. Нэмэлт мэдээлэл авах"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Хаана <xliff:g id="CREATETYPES">%1$s</xliff:g>-г сонгоно уу"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"нууц үгээ хадгалах"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"нэвтрэх мэдээллээ хадгалах"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-д passkey үүсгэх үү?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Нууц үгээ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-д хадгалах уу?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Нэвтрэх мэдээллээ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-д хадгалах уу?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Та өөрийн <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>-г дурын төхөөрөмж дээр ашиглах боломжтой. Үүнийг <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>-д <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-д зориулж хадгалсан"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"нууц үг"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"нэвтрэлт"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-г бүх нэвтрэлтдээ ашиглах уу?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Өгөгдмөлөөр тохируулах"</string>
+    <string name="use_once" msgid="9027366575315399714">"Нэг удаа ашиглах"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> нууц үг, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkey"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> нууц үг"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkey"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Өөр төхөөрөмж"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Нууц үгний бусад менежер"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Хүснэгтийг хаах"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Өмнөх хуудас руу буцах"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д өөрийн хадгалсан passkey-г ашиглах уу?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д хадгалсан нэвтрэх мэдээллээ ашиглах уу?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д зориулж хадгалсан нэвтрэх мэдээллийг сонгоно уу"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Өөр аргаар нэвтрэх"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Үгүй, баярлалаа"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Үргэлжлүүлэх"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Нэвтрэх сонголт"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-д"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Түгжээтэй нууц үгний менежерүүд"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Түгжээг тайлахын тулд товшино уу"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Нэвтрэлтийг удирдах"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Өөр төхөөрөмжөөс"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Өөр төхөөрөмж ашиглах"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
new file mode 100644
index 0000000..c4d12f5
--- /dev/null
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"रद्द करा"</string>
+    <string name="string_continue" msgid="1346732695941131882">"पुढे सुरू ठेवा"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"दुसऱ्या ठिकाणी तयार करा"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"दुसऱ्या ठिकाणी सेव्ह करा"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"दुसरे डिव्‍हाइस वापरा"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"दुसऱ्या डिव्हाइसवर सेव्ह करा"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"सुरक्षितपणे साइन इन करण्याचा सोपा मार्ग"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"युनिक पासकीसह साइन इन करण्यासाठी तुमचे फिंगरप्रिंट, फेस किंवा स्क्रीन लॉक वापरा, जे विसरता येणार नाही किंवा चोरीला जाणार नाही. अधिक जाणून घ्या"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> कुठे करायचे ते निवडा"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"तुमचा पासवर्ड सेव्ह करा"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"तुमची साइन-इन माहिती सेव्ह करा"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मध्ये पासकी तयार करायची का?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"तुमचा पासवर्ड <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> वर सेव्ह करायचा का?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"तुमची साइन-इन माहिती <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> वर सेव्ह करायची का?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"तुम्ही कोणत्याही डिव्हाइसवर तुमचे <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> वापरू शकता. ते <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> वर सेव्ह केले जाते"</string>
+    <string name="passkey" msgid="632353688396759522">"पासकी"</string>
+    <string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"साइन-इन"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"तुमच्या सर्व साइन-इन साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>वापरायचे का?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"डिफॉल्ट म्हणून सेट करा"</string>
+    <string name="use_once" msgid="9027366575315399714">"एकदा वापरा"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> पासकी"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> पासकी"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"इतर डिव्हाइस"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"इतर पासवर्ड व्यवस्थापक"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"शीट बंद करा"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"मागील पेजवर परत जा"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमची सेव्ह केलेली पासकी वापरायची का?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमचे सेव्ह केलेले साइन-इन वापरायचे का?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सेव्ह केलेले साइन-इन निवडा"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"दुसऱ्या मार्गाने साइन इन करा"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"नाही, नको"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"पुढे सुरू ठेवा"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन इन पर्याय"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक केलेले पासवर्ड व्यवस्थापक"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"अनलॉक करण्यासाठी टॅप करा"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन-इन व्यवस्थापित करा"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"दुसऱ्या डिव्हाइस वरून"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"वेगळे डिव्हाइस वापरा"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml
new file mode 100644
index 0000000..fb130fe
--- /dev/null
+++ b/packages/CredentialManager/res/values-ms/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Batal"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Teruskan"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Buat di tempat lain"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Simpan di tempat lain"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Gunakan peranti lain"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Simpan kepada peranti lain"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Cara mudah untuk log masuk dengan selamat"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gunakan cap jari, wajah atau kunci skrin anda untuk log masuk menggunakan kunci laluan unik yang tidak boleh dilupakan atau dicuri. Ketahui lebih lanjut"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Pilih tempat untuk <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"simpan kata laluan anda"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"simpan maklumat log masuk anda"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Buat kunci laluan dalam <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Simpan kata laluan anda pada <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Simpan maklumat log masuk anda pada <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Anda boleh menggunakan <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> anda pada mana-mana peranti. Ia disimpan pada <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> untuk <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"kunci laluan"</string>
+    <string name="password" msgid="6738570945182936667">"kata laluan"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"log masuk"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gunakan <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> untuk semua log masuk anda?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Tetapkan sebagai lalai"</string>
+    <string name="use_once" msgid="9027366575315399714">"Gunakan sekali"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Kata laluan <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, kunci laluan <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Kata laluan <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Kunci laluan <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Peranti lain"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Password Manager lain"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Tutup helaian"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci laluan anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan maklumat log masuk anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih log masuk yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Log masuk menggunakan cara lain"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Tidak perlu"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Teruskan"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Pilihan log masuk"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Untuk <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Password Manager dikunci"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Ketik untuk membuka kunci"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Urus log masuk"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Daripada peranti lain"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan peranti yang lain"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
new file mode 100644
index 0000000..b14960a
--- /dev/null
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"မလုပ်တော့"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ရှေ့ဆက်ရန်"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"နောက်တစ်နေရာတွင် ပြုလုပ်ရန်"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"နောက်တစ်နေရာတွင် သိမ်းရန်"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"စက်နောက်တစ်ခု သုံးရန်"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"စက်နောက်တစ်ခုတွင် သိမ်းရန်"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"လုံခြုံစွာလက်မှတ်ထိုးဝင်ရန် ရိုးရှင်းသောနည်း"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"မေ့မသွား (သို့) ခိုးမသွားနိုင်သော သီးခြားလျှို့ဝှက်ကီးဖြင့် လက်မှတ်ထိုးဝင်ရန် သင့်လက်ဗွေ၊ မျက်နှာ (သို့) ဖန်သားပြင်လော့ခ် သုံးနိုင်သည်။ ပိုမိုလေ့လာရန်"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ရန် နေရာရွေးပါ"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"သင့်စကားဝှက် သိမ်းရန်"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"သင်၏ လက်မှတ်ထိုးဝင်သည့်အချက်အလက်ကို သိမ်းရန်"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> တွင် လျှို့ဝှက်ကီး ပြုလုပ်မလား။"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"သင့်စကားဝှက်ကို <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> တွင် သိမ်းမလား။"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"သင်၏ လက်မှတ်ထိုးဝင်သည့်အချက်အလက်ကို <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> တွင် သိမ်းမလား။"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"မည်သည့်စက်တွင်မဆို သင်၏ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ကို သုံးနိုင်သည်။ ၎င်းကို <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> အတွက် <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> တွင် သိမ်းလိုက်သည်"</string>
+    <string name="passkey" msgid="632353688396759522">"လျှို့ဝှက်ကီး"</string>
+    <string name="password" msgid="6738570945182936667">"စကားဝှက်"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"လက်မှတ်ထိုးဝင်မှုများ"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"သင်၏လက်မှတ်ထိုးဝင်မှု အားလုံးအတွက် <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> သုံးမလား။"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"မူရင်းအဖြစ် သတ်မှတ်ရန်"</string>
+    <string name="use_once" msgid="9027366575315399714">"တစ်ကြိမ်သုံးရန်"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"စကားဝှက် <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ခု၊ လျှို့ဝှက်ကီး <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ခု"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"စကားဝှက် <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ခု"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"လျှို့ဝှက်ကီး <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ခု"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"စက်နောက်တစ်ခု"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"အခြားစကားဝှက်မန်နေဂျာများ"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"စာမျက်နှာ ပိတ်ရန်"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ယခင်စာမျက်နှာကို ပြန်သွားပါ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလျှို့ဝှက်ကီး သုံးမလား။"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလက်မှတ်ထိုးဝင်မှု သုံးမလား။"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသော လက်မှတ်ထိုးဝင်မှုကို ရွေးပါ"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"နောက်တစ်နည်းဖြင့် လက်မှတ်ထိုးဝင်ရန်"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"မလိုပါ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ရှေ့ဆက်ရန်"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"လက်မှတ်ထိုးဝင်ရန် နည်းလမ်းများ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> အတွက်"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"လော့ခ်ချထားသည့် စကားဝှက်မန်နေဂျာများ"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ဖွင့်ရန် တို့ပါ"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"လက်မှတ်ထိုးဝင်မှုများ စီမံခြင်း"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"စက်နောက်တစ်ခုမှ"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"အခြားစက်သုံးရန်"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
new file mode 100644
index 0000000..d53bc7e
--- /dev/null
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Avbryt"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Fortsett"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Opprett på et annet sted"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Lagre på et annet sted"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Bruk en annen enhet"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"En enkel og trygg påloggingsmåte"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Bruk fingeravtrykk, ansiktet eller en skjermlås til å logge på med en unik tilgangsnøkkel du verken kan glemme eller miste. Finn ut mer"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Velg hvor <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"lagre passordet ditt"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"lagre påloggingsinformasjonen din"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vil du opprette en tilgangsnøkkel i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vil du lagre passordet i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vil du lagre påloggingsinformasjonen i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Du kan bruke <xliff:g id="TYPE">%2$s</xliff:g>-elementet for <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> på alle slags enheter. Den lagres i <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"tilgangsnøkkel"</string>
+    <string name="password" msgid="6738570945182936667">"passord"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"pålogginger"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vil du bruke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for alle pålogginger?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Angi som standard"</string>
+    <string name="use_once" msgid="9027366575315399714">"Bruk én gang"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passord, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> tilgangsnøkler"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passord"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> tilgangsnøkler"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"En annen enhet"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Andre løsninger for passordlagring"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Lukk arket"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbake til den forrige siden"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruke den lagrede tilgangsnøkkelen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruke den lagrede påloggingen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Velg en lagret pålogging for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Bruk en annen påloggingsmetode"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nei takk"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Fortsett"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Påloggingsalternativer"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låste løsninger for passordlagring"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Trykk for å låse opp"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrer pålogginger"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en annen enhet"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Bruk en annen enhet"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml
new file mode 100644
index 0000000..77b0959
--- /dev/null
+++ b/packages/CredentialManager/res/values-ne/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"रद्द गर्नुहोस्"</string>
+    <string name="string_continue" msgid="1346732695941131882">"जारी राख्नुहोस्"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"अर्को ठाउँमा बनाउनुहोस्"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"अर्को ठाउँमा सेभ गर्नुहोस्"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"अर्को डिभाइस प्रयोग गर्नुहोस्"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"अर्को डिभाइसमा सेभ गर्नुहोस्"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"सुरक्षित तरिकाले साइन इन गर्ने सरल तरिका"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"नभुलिने वा चोरी नहुने खालको अद्वितीय पासकीका साथै आफ्ना फिंगरप्रिन्ट, अनुहार वा स्क्रिन लक प्रयोग गरी साइन इन गर्नुहोस्। थप जान्नुहोस्"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> सेभ गर्ने ठाउँ छनौट गर्नुहोस्"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"आफ्नो पासवर्ड सेभ गर्नुहोस्"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"आफ्नो साइन इनसम्बन्धी जानकारी सेभ गर्नुहोस्"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मा पासकी बनाउने हो?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"तपाईंको पासवर्ड <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मा सेभ गर्ने हो?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"तपाईंको साइन इनसम्बन्धी जानकारी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मा सेभ गर्ने हो?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"तपाईं जुनसुकै डिभाइसमा आफ्नो <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> प्रयोग गर्न सक्नुहुन्छ। यसलाई <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> का लागि <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> मा सेभ गरिएको छ"</string>
+    <string name="passkey" msgid="632353688396759522">"पासकी"</string>
+    <string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"साइन इनसम्बन्धी जानकारी"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"तपाईंले साइन इन गर्ने सबै डिभाइसहरूमा <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> प्रयोग गर्ने हो?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"डिफल्ट जानकारीका रूपमा सेट गर्नुहोस्"</string>
+    <string name="use_once" msgid="9027366575315399714">"एक पटक प्रयोग गर्नुहोस्"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> वटा पासवर्ड, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> वटा पासकी"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> वटा पासवर्ड"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> वटा पासकी"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"अर्को डिभाइस"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"अन्य पासवर्ड म्यानेजरहरू"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"पाना बन्द गर्नुहोस्"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"अघिल्लो पेजमा फर्कनुहोस्"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"आफूले सेभ गरेको पासकी प्रयोग गरी <xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्ने हो?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"आफूले सेभ गरेको साइन इनसम्बन्धी जानकारी प्रयोग गरी <xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्ने हो?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्नका लागि सेभ गरिएका साइन इनसम्बन्धी जानकारी छनौट गर्नुहोस्"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"अर्कै विधि प्रयोग गरी साइन इन गर्नुहोस्"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"पर्दैन, धन्यवाद"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"जारी राख्नुहोस्"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन‍ इनसम्बन्धी विकल्पहरू"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> का लागि"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लक गरिएका पासवर्ड म्यानेजरहरू"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"अनलक गर्न ट्याप गर्नुहोस्"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इनसम्बन्धी विकल्पहरू व्यवस्थापन गर्नुहोस्"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"अर्को डिभाइसका लागि"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"अर्कै डिभाइस प्रयोग गरी हेर्नुहोस्"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml
new file mode 100644
index 0000000..a80c288
--- /dev/null
+++ b/packages/CredentialManager/res/values-nl/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Annuleren"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Doorgaan"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Op een andere locatie maken"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Op een andere locatie opslaan"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Een ander apparaat gebruiken"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Een makkelijke manier om beveiligd in te loggen"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gebruik je vingerafdruk, gezichtsvergrendeling of schermvergrendeling om in te loggen met een unieke toegangssleutel die je niet kunt vergeten en die anderen niet kunnen stelen. Meer informatie"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Een locatie kiezen voor <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"je wachtwoord opslaan"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"je inloggegevens opslaan"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Een toegangssleutel maken in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Je wachtwoord opslaan in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Je inloggegevens opslaan in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Je kunt je <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> op elk apparaat gebruiken. Het wordt opgeslagen in <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> voor <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"toegangssleutel"</string>
+    <string name="password" msgid="6738570945182936667">"wachtwoord"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"inloggegevens"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> elke keer gebruiken als je inlogt?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Instellen als standaard"</string>
+    <string name="use_once" msgid="9027366575315399714">"Eén keer gebruiken"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wachtwoorden, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> toegangssleutels"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wachtwoorden"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> toegangssleutels"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Een ander apparaat"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Andere wachtwoordmanagers"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Blad sluiten"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Ga terug naar de vorige pagina"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Je opgeslagen toegangssleutel voor <xliff:g id="APP_NAME">%1$s</xliff:g> gebruiken?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Je opgeslagen inloggegevens voor <xliff:g id="APP_NAME">%1$s</xliff:g> gebruiken?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Opgeslagen inloggegevens kiezen voor <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Inloggen via een andere methode"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nee, bedankt"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Doorgaan"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opties voor inloggen"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Voor <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Vergrendelde wachtwoordmanagers"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tik om te ontgrendelen"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Inloggegevens beheren"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via een ander apparaat"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Een ander apparaat gebruiken"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml
new file mode 100644
index 0000000..5b401db
--- /dev/null
+++ b/packages/CredentialManager/res/values-or/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ବାତିଲ କରନ୍ତୁ"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"ଅନ୍ୟ ଏକ ସ୍ଥାନରେ ତିଆରି କରନ୍ତୁ"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"ଅନ୍ୟ ଏକ ସ୍ଥାନରେ ସେଭ କରନ୍ତୁ"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"ଅନ୍ୟ ଏହି ଡିଭାଇସ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"ଅନ୍ୟ ଏକ ଡିଭାଇସରେ ସେଭ କରନ୍ତୁ"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"ସୁରକ୍ଷିତ ଭାବେ ସାଇନ ଇନ କରିବାର ଏକ ସରଳ ଉପାୟ"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ଏକ ସ୍ୱତନ୍ତ୍ର ପାସକୀ ମାଧ୍ୟମରେ ସାଇନ ଇନ କରିବା ପାଇଁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ, ଫେସ କିମ୍ବା ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ ଯାହାକୁ ଭୁଲି ପାରିବେ ନାହିଁ କିମ୍ବା ଚୋରି ହୋଇପାରିବ ନାହିଁ। ଅଧିକ ଜାଣନ୍ତୁ"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"କେଉଁଠି <xliff:g id="CREATETYPES">%1$s</xliff:g> କରିବେ, ତାହା ବାଛନ୍ତୁ"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ଆପଣଙ୍କ ପାସୱାର୍ଡ ସେଭ କରନ୍ତୁ"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ଆପଣଙ୍କ ସାଇନ-ଇନ ସୂଚନା ସେଭ କରନ୍ତୁ"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ରେ ଏକ ପାସକୀ ତିଆରି କରିବେ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"ଆପଣଙ୍କ ପାସୱାର୍ଡକୁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ରେ ସେଭ କରିବେ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ଆପଣଙ୍କ ସାଇନ-ଇନ ସୂଚନାକୁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ରେ ସେଭ କରିବେ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"ଆପଣ ଯେ କୌଣସି ଡିଭାଇସରେ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>କୁ ବ୍ୟବହାର କରିପାରିବେ। ଏହାକୁ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ପାଇଁ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>ରେ ସେଭ କରାଯାଏ"</string>
+    <string name="passkey" msgid="632353688396759522">"ପାସକୀ"</string>
+    <string name="password" msgid="6738570945182936667">"ପାସୱାର୍ଡ"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ସାଇନ-ଇନ"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ଆପଣଙ୍କ ସମସ୍ତ ସାଇନ-ଇନ ପାଇଁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବେ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ଡିଫଲ୍ଟ ଭାବେ ସେଟ କରନ୍ତୁ"</string>
+    <string name="use_once" msgid="9027366575315399714">"ଥରେ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ଟି ପାସକୀ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>ଟି ପାସକୀ"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"ଅନ୍ୟ ଏକ ଡିଭାଇସ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"ଅନ୍ୟ Password Manager"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ସିଟ ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ପୂର୍ବବର୍ତ୍ତୀ ପୃଷ୍ଠାକୁ ଫେରନ୍ତୁ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଆପଣଙ୍କ ପାସକୀ ବ୍ୟବହାର କରିବେ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଆପଣଙ୍କ ସାଇନ-ଇନ ବ୍ୟବହାର କରିବେ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଏକ ସାଇନ-ଇନ ବାଛନ୍ତୁ"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ଅନ୍ୟ ଏକ ଉପାୟରେ ସାଇନ ଇନ କରନ୍ତୁ"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ନା, ଧନ୍ୟବାଦ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ସାଇନ ଇନ ବିକଳ୍ପଗୁଡ଼ିକ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>ରେ"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ଲକ ଥିବା Password Manager"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ଅନଲକ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ସାଇନ-ଇନ ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ଅନ୍ୟ ଏକ ଡିଭାଇସରୁ"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ଏକ ଭିନ୍ନ ଡିଭାଇସ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml
new file mode 100644
index 0000000..f8f5ba1
--- /dev/null
+++ b/packages/CredentialManager/res/values-pa/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ਰੱਦ ਕਰੋ"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ਜਾਰੀ ਰੱਖੋ"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"ਕਿਸੇ ਹੋਰ ਥਾਂ \'ਤੇ ਬਣਾਓ"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"ਕਿਸੇ ਹੋਰ ਥਾਂ \'ਤੇ ਰੱਖਿਅਤ ਕਰੋ"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"ਕੋਈ ਹੋਰ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"ਸੁਰੱਖਿਅਤ ਢੰਗ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਦਾ ਆਸਾਨ ਤਰੀਕਾ"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ਵਿਲੱਖਣ ਪਾਸਕੀ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਵਾਸਤੇ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ, ਚਿਹਰੇ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ ਜਿਸਨੂੰ ਭੁੱਲਿਆ ਜਾਂ ਚੋਰੀ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਹੋਰ ਜਾਣੋ"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ਲਈ ਕੋਈ ਥਾਂ ਚੁਣੋ"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ਆਪਣਾ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰੋ"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ਆਪਣੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰੋ"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"ਕੀ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਵਿੱਚ ਪਾਸਕੀ ਨੂੰ ਬਣਾਉਣਾ ਹੈ?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"ਕੀ ਆਪਣੇ ਪਾਸਵਰਡ ਨੂੰ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ਕੀ ਆਪਣੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਨੂੰ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਡੀਵਾਈਸ \'ਤੇ ਆਪਣੀ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੇ ਹੋ। ਇਸਨੂੰ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ਲਈ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ਵਿੱਚ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="passkey" msgid="632353688396759522">"ਪਾਸਕੀ"</string>
+    <string name="password" msgid="6738570945182936667">"ਪਾਸਵਰਡ"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ਸਾਈਨ-ਇਨਾਂ ਦੀ ਜਾਣਕਾਰੀ"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ਕੀ ਆਪਣੇ ਸਾਰੇ ਸਾਈਨ-ਇਨਾਂ ਲਈ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
+    <string name="use_once" msgid="9027366575315399714">"ਇੱਕ ਵਾਰ ਵਰਤੋ"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ਪਾਸਵਰਡ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ਪਾਸਕੀਆਂ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ਪਾਸਵਰਡ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ਪਾਸਕੀਆਂ"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"ਹੋਰ ਡੀਵਾਈਸ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"ਹੋਰ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ਸ਼ੀਟ ਬੰਦ ਕਰੋ"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ਪਿਛਲੇ ਪੰਨੇ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਆਪਣੀ ਰੱਖਿਅਤ ਕੀਤੀ ਪਾਸਕੀ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਆਪਣੀ ਰੱਖਿਅਤ ਕੀਤੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਰੱਖਿਅਤ ਕੀਤੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਚੁਣੋ"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ਕਿਸੇ ਹੋਰ ਤਰੀਕੇ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ਜਾਰੀ ਰੱਖੋ"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ਸਾਈਨ-ਇਨ ਕਰਨ ਦੇ ਵਿਕਲਪ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ਲਾਕ ਕੀਤੇ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ਸਾਈਨ-ਇਨਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ਹੋਰ ਡੀਵਾਈਸ ਤੋਂ"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ਵੱਖਰੇ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml
new file mode 100644
index 0000000..e684f23
--- /dev/null
+++ b/packages/CredentialManager/res/values-pl/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Anuluj"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Dalej"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Utwórz w innym miejscu"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Zapisz w innym miejscu"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Użyj innego urządzenia"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Zapisz na innym urządzeniu"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Prosty sposób na bezpieczne logowanie"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Używaj odcisku palca, rozpoznawania twarzy lub blokady ekranu, aby logować się z wykorzystaniem unikalnego klucza, którego nie można zapomnieć ani utracić w wyniku kradzieży. Więcej informacji"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Wybierz, gdzie chcesz <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"zapisać hasło"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"zapisać dane logowania"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Utworzyć klucz w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Zapisać hasło w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Zapisać dane logowania w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Elementu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> typu <xliff:g id="TYPE">%2$s</xliff:g> możesz używać na każdym urządzeniu. Jest zapisany w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)"</string>
+    <string name="passkey" msgid="632353688396759522">"klucz"</string>
+    <string name="password" msgid="6738570945182936667">"hasło"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"dane logowania"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Używać usługi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> w przypadku wszystkich danych logowania?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Ustaw jako domyślną"</string>
+    <string name="use_once" msgid="9027366575315399714">"Użyj raz"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Hasła: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, klucze: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Hasła: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Klucze: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Inne urządzenie"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Inne menedżery haseł"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zamknij arkusz"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Wróć do poprzedniej strony"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Użyć zapisanego klucza dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Użyć zapisanych danych logowania dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Wybierz zapisane dane logowania dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Zaloguj się w inny sposób"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nie, dziękuję"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Dalej"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcje logowania"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zablokowane menedżery haseł"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Kliknij, aby odblokować"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Zarządzanie danymi logowania"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na innym urządzeniu"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Użyj innego urządzenia"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..570d0e6
--- /dev/null
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Criar em outro lugar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvar em outro lugar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvar em outro dispositivo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Uma maneira simples de fazer login com segurança"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use a impressão digital, o reconhecimento facial ou um bloqueio de tela para fazer login com uma única chave de acesso que não pode ser esquecida ou perdida. Saiba mais"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Escolha onde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"salvar sua senha"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"salvar suas informações de login"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Criar uma chave de acesso em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Salvar sua senha em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Salvar suas informações de login em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Você pode usar seu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> em qualquer dispositivo. Ele está salvo em <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
+    <string name="password" msgid="6738570945182936667">"senha"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"logins"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus logins?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Outros gerenciadores de senha"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Fechar página"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Voltar à página anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar sua chave de acesso salva para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar suas informações de login salvas para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolher um login salvo para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Fazer login de outra forma"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Agora não"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opções de login"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gerenciadores de senha bloqueados"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toque para desbloquear"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gerenciar logins"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar um dispositivo diferente"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..8a105cd
--- /dev/null
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string>
+    <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Criar noutro lugar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Guardar noutro lugar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Guardar noutro dispositivo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Uma forma simples de iniciar sessão em segurança"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use a sua impressão digital, rosto ou bloqueio de ecrã para iniciar sessão com uma chave de acesso única que não pode ser esquecida nem perdida. Saiba mais"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Escolha onde quer guardar <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"guardar a sua palavra-passe"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"guardar as suas informações de início de sessão"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Criar uma chave de acesso em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Guardar a sua palavra-passe em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Guardar as suas informações de início de sessão em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Pode usar <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> em qualquer dispositivo. É guardado em <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
+    <string name="password" msgid="6738570945182936667">"palavra-passe"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"inícios de sessão"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus inícios de sessão?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Definir como predefinição"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> palavras-passe, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> palavras-passe"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+    <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string>
+    <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Outros gestores de palavras-passe"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Fechar folha"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volte à página anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar a sua chave de acesso guardada na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar o seu início de sessão guardado na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolha um início de sessão guardado para a app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Iniciar sessão de outra forma"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Não"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opções de início de sessão"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestores de palavras-passe bloqueados"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toque para desbloquear"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Faça a gestão dos inícios de sessão"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use um dispositivo diferente"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
new file mode 100644
index 0000000..570d0e6
--- /dev/null
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Criar em outro lugar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvar em outro lugar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvar em outro dispositivo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Uma maneira simples de fazer login com segurança"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use a impressão digital, o reconhecimento facial ou um bloqueio de tela para fazer login com uma única chave de acesso que não pode ser esquecida ou perdida. Saiba mais"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Escolha onde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"salvar sua senha"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"salvar suas informações de login"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Criar uma chave de acesso em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Salvar sua senha em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Salvar suas informações de login em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Você pode usar seu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> em qualquer dispositivo. Ele está salvo em <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
+    <string name="password" msgid="6738570945182936667">"senha"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"logins"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus logins?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
+    <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Outros gerenciadores de senha"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Fechar página"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Voltar à página anterior"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar sua chave de acesso salva para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar suas informações de login salvas para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolher um login salvo para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Fazer login de outra forma"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Agora não"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opções de login"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gerenciadores de senha bloqueados"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toque para desbloquear"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gerenciar logins"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar um dispositivo diferente"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
new file mode 100644
index 0000000..51955d4
--- /dev/null
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Anulează"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Continuă"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Creează în alt loc"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvează în alt loc"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Folosește alt dispozitiv"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvează pe alt dispozitiv"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un mod simplu de a te conecta în siguranță"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Folosește-ți amprenta, fața sau blocarea ecranului ca să te conectezi cu o cheie de acces unică, pe care nu o poți uita și care nu poate fi furată. Află mai multe"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Alege unde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"salvează parola"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"salvează informațiile de conectare"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Creezi o cheie de acces în <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Salvezi parola în <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Salvezi informațiile de conectare în <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Poți folosi <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> pe orice dispozitiv. Se salvează în <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pentru <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"cheie de acces"</string>
+    <string name="password" msgid="6738570945182936667">"parolă"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"date de conectare"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Folosești <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pentru toate conectările?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Setează ca prestabilite"</string>
+    <string name="use_once" msgid="9027366575315399714">"Folosește o dată"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parole, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chei de acces"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parole"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chei de acces"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Alt dispozitiv"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Alți manageri de parole"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Închide foaia"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revino la pagina precedentă"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Folosești cheia de acces salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Folosești datele de conectare salvate pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Alege o conectare salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Conectează-te altfel"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nu, mulțumesc"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuă"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opțiuni de conectare"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pentru <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Manageri de parole blocate"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Atinge pentru a debloca"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionează acreditările"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De pe alt dispozitiv"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Folosește alt dispozitiv"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
new file mode 100644
index 0000000..2a459c1
--- /dev/null
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Отмена"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Продолжить"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Создать в другом месте"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Сохранить в другом месте"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Использовать другое устройство"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Простой и безопасный способ входа"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"С уникальным ключом доступа, который невозможно украсть или забыть, вы можете подтверждать свою личность по отпечатку пальца, с помощью фейсконтроля или блокировки экрана. Подробнее…"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Выберите, где <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"сохранить пароль"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"сохранить данные для входа"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Создать ключ доступа в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Сохранить пароль в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Сохранить учетные данные в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Вы можете использовать <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на любом устройстве. Данные <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> сохраняются в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\"."</string>
+    <string name="passkey" msgid="632353688396759522">"ключ доступа"</string>
+    <string name="password" msgid="6738570945182936667">"пароль"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"входы"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Всегда входить с помощью приложения \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Использовать по умолчанию"</string>
+    <string name="use_once" msgid="9027366575315399714">"Использовать один раз"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Пароли (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) и ключи доступа (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Пароли (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Ключи доступа (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Другое устройство"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Другие менеджеры паролей"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Закрыть лист"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вернуться на предыдущую страницу"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Использовать сохраненный ключ доступа для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Использовать сохраненные учетные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Выберите сохраненные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Войти другим способом"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Нет, спасибо"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Продолжить"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Варианты входа"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Для пользователя <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблокированные менеджеры паролей"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Нажмите для разблокировки"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управление входом"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"С другого устройства"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Использовать другое устройство"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml
new file mode 100644
index 0000000..de5a5a2
--- /dev/null
+++ b/packages/CredentialManager/res/values-si/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"අවලංගු කරන්න"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ඉදිරියට යන්න"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"වෙනත් ස්ථානයක තනන්න"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"වෙනත් ස්ථානයකට සුරකින්න"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"වෙනත් උපාංගයක් භාවිතා කරන්න"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"සුරක්ෂිතව පුරනය වීමට සරල ක්‍රමයක්"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"අමතක කළ නොහැකි හෝ සොරකම් කළ නොහැකි අනන්‍ය මුරයතුරක් සමග පුරනය වීමට ඔබේ ඇඟිලි සලකුණ, මුහුණ හෝ තිර අගුල භාවිතා කරන්න. තව දැන ගන්න⁠"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> කොතැනක ද යන්න තෝරා ගන්න"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ඔබේ මුරපදය සුරකින්න"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ඔබේ පුරනය වීමේ තතු සුරකින්න"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> තුළ මුරයතුරක් තනන්න ද?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"ඔබේ මුරපදය <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> වෙත සුරකින්න ද?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ඔබේ පුරනය වීමේ තතු <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> වෙත සුරකින්න ද?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"ඔබට ඕනෑම උපාංගයක ඔබේ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> භාවිතා කළ හැක. එය <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> සඳහා <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> වෙත සුරැකෙයි"</string>
+    <string name="passkey" msgid="632353688396759522">"මුරයතුර"</string>
+    <string name="password" msgid="6738570945182936667">"මුරපදය"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"පුරනය වීම්"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ඔබේ සියලු පුරනය වීම් සඳහා <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> භාවිතා කරන්න ද?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"පෙරනිමි ලෙස සකසන්න"</string>
+    <string name="use_once" msgid="9027366575315399714">"වරක් භාවිතා කරන්න"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"මුරපද <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ක්, මුරයතුරු <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ක්"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"මුරපද <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ක්"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"මුරයතුරු <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>ක්"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"වෙනත් උපාංගයක්"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"වෙනත් මුරපද කළමනාකරුවන්"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"පත්‍රය වසන්න"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"පෙර පිටුවට ආපසු යන්න"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා ඔබේ සුරැකි මුරයතුර භාවිතා කරන්න ද?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා ඔබේ සුරැකි පුරනය භාවිතා කරන්න ද?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා සුරැකි පුරනයක් තෝරා ගන්න"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"වෙනත් ආකාරයකින් පුරන්න"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"එපා ස්තුතියි"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ඉදිරියට යන්න"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"පුරනය වීමේ විකල්ප"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> සඳහා"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"අගුළු දැමූ මුරපද කළමනාකරුවන්"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"අගුළු හැරීමට තට්ටු කරන්න"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"පුරනය වීම් කළමනාකරණය කරන්න"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"වෙනත් උපාංගයකින්"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"වෙනස් උපාංගයක් භාවිතා කරන්න"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml
new file mode 100644
index 0000000..4545868
--- /dev/null
+++ b/packages/CredentialManager/res/values-sk/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Zrušiť"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Pokračovať"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Vytvoriť inde"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Uložiť inde"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Použiť iné zariadenie"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednoduchý spôsob bezpečného prihlasovania"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Použite odtlačok prsta, tvár alebo zámku obrazovky a prihláste sa jedinečným prístupovým kľúčom, ktorý sa nedá zabudnúť ani ukradnúť. Ďalšie informácie"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Vyberte, kam <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"uložiť heslo"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"uložiť prihlasovacie údaje"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Chcete vytvoriť prístupový kľúč v službe <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Chcete uložiť heslo do služby <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Chcete uložiť svoje prihlasovacie údaje do služby <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> môžete používať v ľubovoľnom zariadení. Ukladá sa do služby <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pre <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+    <string name="passkey" msgid="632353688396759522">"prístupový kľúč"</string>
+    <string name="password" msgid="6738570945182936667">"heslo"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"prihlasovacie údaje"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Chcete pre všetky svoje prihlasovacie údaje použiť <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Nastaviť ako predvolené"</string>
+    <string name="use_once" msgid="9027366575315399714">"Použiť raz"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Počet hesiel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, počet prístupových kľúčov <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Počet hesiel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Počet prístupových kľúčov: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Iné zariadenie"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Iní správcovia hesiel"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zavrieť hárok"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Prejsť späť na predchádzajúcu stránku"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Chcete pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> použiť uložený prístupový kľúč?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Chcete pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> použiť uložené prihlasovacie údaje?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vyberte uložené prihlasovacie údaje pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prihláste sa inak"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nie, vďaka"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Pokračovať"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Možnosti prihlásenia"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pre používateľa <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Správcovia uzamknutých hesiel"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Odomknite klepnutím"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Spravovať prihlasovacie údaje"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z iného zariadenia"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použiť iné zariadenie"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml
new file mode 100644
index 0000000..94edf66
--- /dev/null
+++ b/packages/CredentialManager/res/values-sl/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Prekliči"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Naprej"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Ustvarjanje na drugem mestu"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Shranjevanje na drugo mesto"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Uporabi drugo napravo"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Shrani v drugo napravo"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Preprost način za varno prijavo"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Če se želite prijaviti z enoličnim ključem za dostop, ki ga ni mogoče pozabiti ali ukrasti, uporabite prstni odtis, obraz ali nastavljeni način za odklepanje zaslona. Več o tem"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Izberite mesto za <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"shranjevanje gesla"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"shranjevanje podatkov za prijavo"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite ustvariti ključ za dostop pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite shraniti geslo pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite shraniti podatke za prijavo pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="TYPE">%2$s</xliff:g> za <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> lahko uporabite v kateri koli napravi. Shranjeno je pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>« za <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+    <string name="passkey" msgid="632353688396759522">"ključ za dostop"</string>
+    <string name="password" msgid="6738570945182936667">"geslo"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite za vse prijave uporabiti »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Nastavi kot privzeto"</string>
+    <string name="use_once" msgid="9027366575315399714">"Uporabi enkrat"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Št. gesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, št. ključev za dostop: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Št. gesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Št. ključev za dostop: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Druga naprava"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji gesel"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Zapri list"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Nazaj na prejšnjo stran"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite uporabiti shranjeni ključ za dostop do aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite uporabiti shranjene podatke za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Izberite shranjene podatke za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijava na drug način"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Naprej"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Možnosti prijave"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zaklenjeni upravitelji gesel"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dotaknite se, da odklenete"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljanje podatkov za prijavo"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Iz druge naprave"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Uporaba druge naprave"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml
new file mode 100644
index 0000000..6b85a90
--- /dev/null
+++ b/packages/CredentialManager/res/values-sq/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Anulo"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Vazhdo"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Krijo në një vend tjetër"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Ruaj në një vend tjetër"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Përdor një pajisje tjetër"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Një mënyrë e thjeshtë për t\'u identifikuar në mënyrë të sigurt"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Përdor gjurmën e gishtit, fytyrën ose kyçjen e ekranit për t\'u identifikuar me një çelës unik kalimi i cili nuk mund të harrohet ose të vidhet. Mëso më shumë"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Zgjidh se ku të <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"ruaj fjalëkalimin"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"ruaj informacionet e tua të identifikimit"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Të krijohet një çelës kalimi në <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Të ruhet fjalëkalimi në <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Të ruhen informacionet e tua të identifikimit në <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Mund të përdorësh <xliff:g id="TYPE">%2$s</xliff:g> të <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> në çdo pajisje. Ai është i ruajtur te <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> për <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"çelësi i kalimit"</string>
+    <string name="password" msgid="6738570945182936667">"fjalëkalimi"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"identifikimet"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Të përdoret <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> për të gjitha identifikimet?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Cakto si parazgjedhje"</string>
+    <string name="use_once" msgid="9027366575315399714">"Përdor një herë"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> fjalëkalime, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> çelësa kalimi"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> fjalëkalime"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> çelësa kalimi"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Një pajisje tjetër"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Menaxherët e tjerë të fjalëkalimeve"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Mbyll fletën"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kthehu te faqja e mëparshme"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Të përdoret fjalëkalimi yt i ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Të përdoret identifikimi yt i ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Zgjidh një identifikim të ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Identifikohu me një mënyrë tjetër"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Jo, faleminderit"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Vazhdo"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opsionet e identifikimit"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Për <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menaxherët e fjalëkalimeve të kyçura"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Trokit për të shkyçur"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Identifikimet e menaxhimit"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Nga një pajisje tjetër"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Përdor një pajisje tjetër"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml
new file mode 100644
index 0000000..79c2eef
--- /dev/null
+++ b/packages/CredentialManager/res/values-sr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Настави"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Направи на другом месту"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Сачувај на другом месту"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Користи други уређај"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Сачувај на други уређај"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Једноставан начин да се безбедно пријављујете"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Користите отисак прста, закључавање лицем или закључавање екрана да бисте се пријавили помоћу јединственог приступног кода који не може да се заборави или украде. Сазнајте више"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Одаберите локацију за: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"сачувајте лозинку"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"сачувајте податке о пријављивању"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Желите да направите приступни кôд код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Желите да сачувате лозинку код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Желите да сачувате податке о пријављивању код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Можете да користите тип домена <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на било ком уређају. Чува се код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"приступни кôд"</string>
+    <string name="password" msgid="6738570945182936667">"лозинка"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"пријављивања"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Желите да за сва пријављивања користите: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Подеси као подразумевано"</string>
+    <string name="use_once" msgid="9027366575315399714">"Користи једном"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, приступних кодова:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Приступних кодова: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Други уређај"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Други менаџери лозинки"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Затворите табелу"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вратите се на претходну страницу"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Желите да користите сачувани приступни кôд за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Желите да користите сачуване податке за пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Одаберите сачувано пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Пријавите се на други начин"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, хвала"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Настави"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опције за пријављивање"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Менаџери закључаних лозинки"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Додирните да бисте откључали"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управљајте пријављивањима"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Са другог уређаја"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Користи други уређај"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml
new file mode 100644
index 0000000..7b25056
--- /dev/null
+++ b/packages/CredentialManager/res/values-sv/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Avbryt"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Fortsätt"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Skapa på en annan plats"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Spara på en annan plats"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Använd en annan enhet"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Ett enkelt sätt att logga in säkert på"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Använd ditt fingeravtryck, ansikte eller skärmlås om du vill logga in med en unik nyckel som inte kan glömmas bort eller bli stulen. Läs mer"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Välj var du <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"spara lösenordet"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"spara inloggningsuppgifterna"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vill du skapa en nyckel i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vill du spara ditt lösenord i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vill du spara dina inloggningsuppgifter i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Du kan använda <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> på alla enheter. Du kan spara det i <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> för <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"nyckel"</string>
+    <string name="password" msgid="6738570945182936667">"lösenord"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"inloggningar"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vill du använda <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> för alla dina inloggningar?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Ange som standard"</string>
+    <string name="use_once" msgid="9027366575315399714">"Använd en gång"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> lösenord, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> nycklar"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> lösenord"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> nycklar"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"En annan enhet"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Andra lösenordshanterare"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Stäng kalkylarket"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tillbaka till föregående sida"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vill du använda din sparade nyckel för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vill du använda dina sparade inloggningsuppgifter för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Välj en sparad inloggning för <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Logga in på ett annat sätt"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nej tack"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Fortsätt"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Inloggningsalternativ"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"För <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låsta lösenordshanterare"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tryck för att låsa upp"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Hantera inloggningar"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via en annan enhet"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Använd en annan enhet"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml
new file mode 100644
index 0000000..646c469
--- /dev/null
+++ b/packages/CredentialManager/res/values-ta/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ரத்துசெய்"</string>
+    <string name="string_continue" msgid="1346732695941131882">"தொடர்க"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"மற்றொரு இடத்தில் உருவாக்கவும்"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"மற்றொரு இடத்தில் சேமிக்கவும்"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"மற்றொரு சாதனத்தைப் பயன்படுத்தவும்"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"வேறொரு சாதனத்தில் சேமியுங்கள்"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"பாதுகாப்பாக உள்நுழைவதற்கான எளிய வழி"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"தனித்துவமான கடவுக்குறியீடு (மறக்காதவை அல்லது திருடமுடியாதவை) மூலம் உள்நுழைய, உங்கள் கைரேகை, முகம் அல்லது திரைப்பூட்டைப் பயன்படுத்தி உள்நுழையவும். மேலும் அறிக"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> எங்கே காட்டப்பட வேண்டும் என்பதைத் தேர்வுசெய்தல்"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"உங்கள் கடவுச்சொல்லைச் சேமிக்கவும்"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"உங்கள் உள்நுழைவு தகவலைச் சேமிக்கவும்"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ல் கடவுக்குறியீட்டை உருவாக்க வேண்டுமா?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"உங்கள் கடவுச்சொல்லை <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ல் சேமிக்க வேண்டுமா?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"உங்கள் உள்நுழைவுத் தகவலை <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ல் சேமிக்க வேண்டுமா?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"உங்கள் <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ஐ எந்தச் சாதனத்தில் வேண்டுமானாலும் பயன்படுத்தலாம். <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>ல் <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>க்காகச் சேமிக்கப்பட்டது"</string>
+    <string name="passkey" msgid="632353688396759522">"கடவுக்குறியீடு"</string>
+    <string name="password" msgid="6738570945182936667">"கடவுச்சொல்"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"உள்நுழைவுகள்"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"உங்கள் அனைத்து உள்நுழைவுகளுக்கும் <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"இயல்பானதாக அமை"</string>
+    <string name="use_once" msgid="9027366575315399714">"ஒருமுறை பயன்படுத்தவும்"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> கடவுச்சொற்கள், <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> கடவுக்குறியீடுகள்"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> கடவுச்சொற்கள்"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> கடவுக்குறியீடுகள்"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"மற்றொரு சாதனம்"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"பிற கடவுச்சொல் நிர்வாகிகள்"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ஷீட்டை மூடும்"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"முந்தைய பக்கத்திற்குச் செல்லும்"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட கடவுக்குறியீட்டைப் பயன்படுத்தவா?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட உள்நுழைவுத் தகவலைப் பயன்படுத்தவா?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட உள்நுழைவுத் தகவலைத் தேர்வுசெய்யவும்"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"வேறு முறையில் உள்நுழைக"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"வேண்டாம்"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"தொடர்க"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"உள்நுழைவு விருப்பங்கள்"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>க்கு"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"பூட்டப்பட்ட கடவுச்சொல் நிர்வாகிகள்"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"அன்லாக் செய்ய தட்டவும்"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"உள்நுழைவுகளை நிர்வகித்தல்"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"மற்றொரு சாதனத்திலிருந்து பயன்படுத்து"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"வேறு சாதனத்தைப் பயன்படுத்து"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml
new file mode 100644
index 0000000..d94f3d3
--- /dev/null
+++ b/packages/CredentialManager/res/values-te/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"రద్దు చేయండి"</string>
+    <string name="string_continue" msgid="1346732695941131882">"కొనసాగించండి"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"మరొక స్థలంలో క్రియేట్ చేయండి"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"మరొక స్థలంలో సేవ్ చేయండి"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"మరొక పరికరాన్ని ఉపయోగించండి"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"మరొక పరికరంలో సేవ్ చేయండి"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"సురక్షితంగా సైన్ ఇన్ చేయడానికి సులభమైన మార్గం"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"మర్చిపోలేని లేదా దొంగిలించలేని ప్రత్యేకమైన పాస్-కీతో సైన్ ఇన్ చేయడానికి మీ వేలిముద్ర, ముఖం లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి. మరింత తెలుసుకోండి"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"ఎక్కడ <xliff:g id="CREATETYPES">%1$s</xliff:g> చేయాలో ఎంచుకోండి"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"మీ పాస్‌వర్డ్‌ను సేవ్ చేయండి"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"మీ సైన్ ఇన్ సమాచారాన్ని సేవ్ చేయండి"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>లో పాస్-కీని క్రియేట్ చేయాలా?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"మీ పాస్‌వర్డ్‌ను <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>కు సేవ్ చేయాలా?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"మీ సైన్ ఇన్ సమాచారాన్ని <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>కు సేవ్ చేయాలా?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"మీరు మీ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>ని ఏ పరికరంలోనైనా ఉపయోగించవచ్చు. ఇది <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> కోసం <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>లో సేవ్ చేయబడింది"</string>
+    <string name="passkey" msgid="632353688396759522">"పాస్-కీ"</string>
+    <string name="password" msgid="6738570945182936667">"పాస్‌వర్డ్"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"సైన్‌ ఇన్‌లు"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"మీ అన్ని సైన్-ఇన్ వివరాల కోసం <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ను ఉపయోగించాలా?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ఆటోమేటిక్ సెట్టింగ్‌గా సెట్ చేయండి"</string>
+    <string name="use_once" msgid="9027366575315399714">"ఒకసారి ఉపయోగించండి"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> పాస్‌వర్డ్‌లు, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> పాస్-కీలు"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> పాస్‌వర్డ్‌లు"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> పాస్-కీలు"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"మరొక పరికరం"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"ఇతర పాస్‌వర్డ్ మేనేజర్‌లు"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"షీట్‌ను మూసివేయండి"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"మునుపటి పేజీకి తిరిగి వెళ్లండి"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీ సేవ్ చేసిన పాస్-కీ వివరాలను ఉపయోగించాలా?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీరు సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఉపయోగించాలా?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఎంచుకోండి"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"మరొక పద్ధతిలో సైన్ ఇన్ చేయండి"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"వద్దు, థ్యాంక్స్"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"కొనసాగించండి"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"సైన్ ఇన్ ఆప్షన్‌లు"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> కోసం"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"లాక్ చేయబడిన పాస్‌వర్డ్ మేనేజర్‌లు"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"అన్‌లాక్ చేయడానికి ట్యాప్ చేయండి"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"సైన్‌ ఇన్‌లను మేనేజ్ చేయండి"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"మరొక పరికరం నుండి"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"వేరే పరికరాన్ని ఉపయోగించండి"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml
new file mode 100644
index 0000000..43f3f0f
--- /dev/null
+++ b/packages/CredentialManager/res/values-th/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"ยกเลิก"</string>
+    <string name="string_continue" msgid="1346732695941131882">"ต่อไป"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"สร้างในตำแหน่งอื่น"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"บันทึกลงในตำแหน่งอื่น"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"ใช้อุปกรณ์อื่น"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"ย้ายไปยังอุปกรณ์อื่น"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"วิธีง่ายๆ ในการลงชื่อเข้าใช้อย่างปลอดภัย"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ใช้ลายนิ้วมือ ใบหน้า หรือล็อกหน้าจอในการลงชื่อเข้าใช้ด้วยพาสคีย์ที่ไม่ซ้ำกันเพื่อไม่ให้ลืมหรือถูกขโมยได้ ดูข้อมูลเพิ่มเติม"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"เลือกตำแหน่งที่จะ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"บันทึกรหัสผ่าน"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"บันทึกข้อมูลการลงชื่อเข้าใช้"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"สร้างพาสคีย์ใน <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ใช่ไหม"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"บันทึกรหัสผ่านลงใน <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ใช่ไหม"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"บันทึกข้อมูลการลงชื่อเข้าใช้ลงใน <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ใช่ไหม"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"คุณใช้ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ในอุปกรณ์ใดก็ได้ โดยจะบันทึกลงใน <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> สำหรับ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"พาสคีย์"</string>
+    <string name="password" msgid="6738570945182936667">"รหัสผ่าน"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"การลงชื่อเข้าใช้"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ใช้ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> สำหรับการลงชื่อเข้าใช้ทั้งหมดใช่ไหม"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"ตั้งเป็นค่าเริ่มต้น"</string>
+    <string name="use_once" msgid="9027366575315399714">"ใช้ครั้งเดียว"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"รหัสผ่าน <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> รายการ พาสคีย์ <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> รายการ"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"รหัสผ่าน <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> รายการ"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"พาสคีย์ <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> รายการ"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"อุปกรณ์อื่น"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"เครื่องมือจัดการรหัสผ่านอื่นๆ"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"ปิดชีต"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"กลับไปยังหน้าก่อนหน้า"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ใช้พาสคีย์ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ใช้การลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"เลือกการลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ลงชื่อเข้าใช้ด้วยวิธีอื่น"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ไม่เป็นไร"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ต่อไป"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ตัวเลือกการลงชื่อเข้าใช้"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"สำหรับ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"เครื่องมือจัดการรหัสผ่านที่ล็อกไว้"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"แตะเพื่อปลดล็อก"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"จัดการการลงชื่อเข้าใช้"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"จากอุปกรณ์อื่น"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ใช้อุปกรณ์อื่น"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml
new file mode 100644
index 0000000..4dae037
--- /dev/null
+++ b/packages/CredentialManager/res/values-tl/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Kanselahin"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Magpatuloy"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Gumawa sa ibang lugar"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"I-save sa ibang lugar"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Gumamit ng ibang device"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"I-save sa ibang device"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Simpleng paraan para mag-sign in lang ligtas"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gamitin ang iyong fingerprint, mukha, o lock ng screen para mag-sign in gamit ang natatanging passkey na hindi makakalimutan o mananakaw. Matuto pa"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Piliin kung saan <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"i-save ang iyong password"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"i-save ang iyong impormasyon sa pag-sign in"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Gumawa ng passkey sa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"I-save ang iyong password sa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"I-save ang iyong impormasyon sa pag-sign in sa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Magagamit mo ang iyong <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> sa anumang device. Naka-save ito sa <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para sa <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"passkey"</string>
+    <string name="password" msgid="6738570945182936667">"password"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"mga sign-in"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gamitin ang <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para sa lahat ng iyong pag-sign in?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Itakda bilang default"</string>
+    <string name="use_once" msgid="9027366575315399714">"Gamitin nang isang beses"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> (na) password, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> (na) passkey"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> (na) password"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> (na) passkey"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Ibang device"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Iba pang password manager"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Isara ang sheet"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Bumalik sa nakaraang page"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gamitin ang iyong naka-save na passkey para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gamitin ang iyong naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pumili ng naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Mag-sign in sa ibang paraan"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Hindi, salamat na lang"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Magpatuloy"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Mga opsyon sa pag-sign in"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para kay <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Mga naka-lock na password manager"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"I-tap para i-unlock"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Pamahalaan ang mga sign-in"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Mula sa ibang device"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gumamit ng ibang device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
new file mode 100644
index 0000000..c1ccd98
--- /dev/null
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"İptal"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Devam"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Başka bir yerde oluşturun"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Başka bir yere kaydedin"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Başka bir cihaz kullan"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Başka bir cihaza kaydedin"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Güvenli bir şekilde oturum açmanın basit yolu"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Parmak iziniz, yüzünüz ya da ekran kilidinizi kullanarak unutması veya çalınması mümkün olmayan benzersiz bir şifre anahtarıyla oturum açın. Daha fazla bilgi"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> yerini seçin"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"şifrenizi kaydedin"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"oturum açma bilgilerinizi kaydedin"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> içinde şifre anahtarı oluşturulsun mu?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Şifreniz <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> içine kaydedilsin mi?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Oturum açma bilgileriniz <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> içine kaydedilsin mi?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> herhangi bir cihazda kullanılabilir. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> için <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> içine kaydedilir"</string>
+    <string name="passkey" msgid="632353688396759522">"şifre anahtarı"</string>
+    <string name="password" msgid="6738570945182936667">"şifre"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"oturum aç"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Tüm oturum açma işlemlerinizde <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kullanılsın mı?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Varsayılan olarak ayarla"</string>
+    <string name="use_once" msgid="9027366575315399714">"Bir kez kullanın"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> şifre anahtarı"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> şifre anahtarı"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Başka bir cihaz"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Diğer şifre yöneticileri"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Sayfayı kapat"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Önceki sayfaya geri dön"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı şifre anahtarınız kullanılsın mı?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgileriniz kullanılsın mı?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgilerini kullanın"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Başka bir yöntemle oturum aç"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Hayır, teşekkürler"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Devam"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Oturum açma seçenekleri"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> için"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Kilitli şifre yöneticileri"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Kilidi açmak için dokunun"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Oturum açma bilgilerini yönetin"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başka bir cihazdan"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Farklı bir cihaz kullan"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml
new file mode 100644
index 0000000..396da4d
--- /dev/null
+++ b/packages/CredentialManager/res/values-uk/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Скасувати"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Продовжити"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Створити в іншому місці"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Зберегти в іншому місці"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Скористатись іншим пристроєм"</string>
+    <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+    <skip />
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Зручний спосіб для безпечного входу"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Користуйтеся відбитком пальця, фейсконтролем або іншим способом розблокування екрана, щоб входити в обліковий запис за допомогою унікального ключа доступу, який неможливо забути чи викрасти. Докладніше"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Виберіть, де <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"зберегти пароль"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"зберегти дані для входу"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Створити ключ доступу в сервісі <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Зберегти ваш пароль у сервісі <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Зберегти ваші дані для входу в сервіс <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Ви можете використовувати <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на будь-якому пристрої. Його збережено в постачальника послуг \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\" для <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
+    <string name="password" msgid="6738570945182936667">"пароль"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"дані для входу"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Використовувати сервіс <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> в усіх випадках входу?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Вибрати за умовчанням"</string>
+    <string name="use_once" msgid="9027366575315399714">"Скористатися раз"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Кількість паролів: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>; кількість ключів доступу: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Кількість паролів: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Кількість ключів доступу: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Інший пристрій"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Інші менеджери паролів"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Закрити аркуш"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Повернутися на попередню сторінку"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Використати збережений ключ доступу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Використати збережені дані для входу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Виберіть збережені дані для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Увійти іншим способом"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ні, дякую"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Продовжити"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опції входу"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Для користувача <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблоковані менеджери паролів"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Торкніться, щоб розблокувати"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Керування даними для входу"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншого пристрою"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Використовувати інший пристрій"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml
new file mode 100644
index 0000000..e67b94c
--- /dev/null
+++ b/packages/CredentialManager/res/values-ur/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"منسوخ کریں"</string>
+    <string name="string_continue" msgid="1346732695941131882">"جاری رکھیں"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"دوسرے مقام میں تخلیق کریں"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"دوسرے مقام میں محفوظ کریں"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"کوئی دوسرا آلہ استعمال کریں"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"دوسرے آلے میں محفوظ کریں"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"محفوظ طریقے سے سائن ان کرنے کا آسان طریقہ"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"اپنے فنگر پرنٹ، چہرے یا اسکرین لاک کا استعمال کریں تاکہ ایک ایسی منفرد پاس کی سے سائن ان کیا جا سکے جسے بھولا یا چوری نہیں کیا جا سکتا۔ مزید جانیں"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> کی جگہ منتخب کریں"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"اپنا پاس ورڈ محفوظ کریں"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"اپنے سائن ان کی معلومات محفوظ کریں"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> میں پاس کی تخلیق کریں؟"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"اپنا پاس ورڈ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> میں محفوظ کریں؟"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"اپنے سائن ان کی معلومات کو <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> میں محفوظ کریں؟"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"آپ کسی بھی آلے پر اپنا <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> استعمال کر سکتے ہیں۔ یہ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> کے <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> میں محفوظ ہو جاتا ہے"</string>
+    <string name="passkey" msgid="632353688396759522">"پاس کی"</string>
+    <string name="password" msgid="6738570945182936667">"پاس ورڈ"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"سائن انز"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"اپنے سبھی سائن انز کے لیے <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> کا استعمال کریں؟"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"بطور ڈیفالٹ سیٹ کریں"</string>
+    <string name="use_once" msgid="9027366575315399714">"ایک بار استعمال کریں"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> پاس ورڈز، <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> پاس کیز"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> پاس ورڈز"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> پاس کیز"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"دوسرا آلہ"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"دیگر پاس ورڈ مینیجرز"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"شیٹ بند کریں"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"گزشتہ صفحے پر واپس جائیں"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنی محفوظ کردہ پاس کی استعمال کریں؟"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنے محفوظ کردہ سائن ان کو استعمال کریں؟"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے محفوظ کردہ سائن انز منتخب کریں"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"دوسرے طریقے سے سائن ان کریں"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"نہیں شکریہ"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"جاری رکھیں"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"سائن ان کے اختیارات"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> کے لیے"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"مقفل کردہ پاس ورڈ مینیجرز"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"غیر مقفل کرنے کے لیے تھپتھپائیں"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"سائن انز کا نظم کریں"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"دوسرے آلے سے"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ایک مختلف آلہ استعمال کریں"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml
new file mode 100644
index 0000000..6c3e211
--- /dev/null
+++ b/packages/CredentialManager/res/values-uz/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Bekor qilish"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Davom etish"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Boshqa joyda yaratish"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Boshqa joyga saqlash"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Boshqa qurilmadan foydalaning"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Boshqa qurilmaga saqlash"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Xavfsiz kirishning oddiy usuli"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Esda qoladigan maxsus kalit bilan kirishda barmoq izi, yuz axboroti yoki ekran qulfidan foydalaning. Batafsil"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> joyini tanlang"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"Parolni saqlash"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"kirish axborotini saqlang"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Kalit <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xizmatida yaratilsinmi?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Parol <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xizmatida saqlansinmi?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Hisob maʼlumotlari <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xizmatida saqlansinmi?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> istalgan qurilmada ishlatilishi mumkin. U <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> uchun <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> xizmatiga saqlandi"</string>
+    <string name="passkey" msgid="632353688396759522">"kalit"</string>
+    <string name="password" msgid="6738570945182936667">"parol"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"kirishlar"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Hamma kirishlarda <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ishlatilsinmi?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Birlamchi deb belgilash"</string>
+    <string name="use_once" msgid="9027366575315399714">"Bir marta ishlatish"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ta parol, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ta kalit"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ta parol"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ta kalit"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Boshqa qurilma"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Boshqa parol menejerlari"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Varaqni yopish"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Avvalgi sahifaga qaytish"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun saqlangan kalit ishlatilsinmi?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun saqlangan maʼlumotlar ishlatilsinmi?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> hisob maʼlumotlarini tanlang"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Boshqa usul orqali kirish"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Kerak emas"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Davom etish"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Kirish parametrlari"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> uchun"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Qulfli parol menejerlari"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Qulfni ochish uchun bosing"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Hisob maʼlumotlarini boshqarish"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Boshqa qurilmada"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Boshqa qurilmadan foydalanish"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml
new file mode 100644
index 0000000..d4703f3
--- /dev/null
+++ b/packages/CredentialManager/res/values-vi/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Huỷ"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Tiếp tục"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Tạo ở vị trí khác"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Lưu vào vị trí khác"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Dùng thiết bị khác"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Lưu vào thiết bị khác"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Cách đơn giản để đăng nhập an toàn"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Dùng vân tay, khuôn mặt hoặc phương thức khoá màn hình để đăng nhập bằng một mã xác thực duy nhất mà bạn không lo sẽ quên hay bị đánh cắp. Tìm hiểu thêm"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Chọn vị trí <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"lưu mật khẩu của bạn"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"lưu thông tin đăng nhập của bạn"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Tạo một mã xác thực trong <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Lưu mật khẩu của bạn vào <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Lưu thông tin đăng nhập của bạn vào <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Bạn có thể sử dụng <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> trên mọi thiết bị. Thông tin này được lưu vào <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> cho <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"mã xác thực"</string>
+    <string name="password" msgid="6738570945182936667">"mật khẩu"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"thông tin đăng nhập"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Dùng <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> cho mọi thông tin đăng nhập của bạn?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Đặt làm mặc định"</string>
+    <string name="use_once" msgid="9027366575315399714">"Dùng một lần"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> mã xác thực"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> mã xác thực"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Thiết bị khác"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Trình quản lý mật khẩu khác"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Đóng trang tính"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Quay lại trang trước"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Dùng mã xác thực bạn đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Dùng thông tin đăng nhập bạn đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Chọn thông tin đăng nhập đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Đăng nhập bằng cách khác"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Không, cảm ơn"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Tiếp tục"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Tuỳ chọn đăng nhập"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Cho <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Trình quản lý mật khẩu đã khoá"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Nhấn để mở khoá"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Quản lý thông tin đăng nhập"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Từ một thiết bị khác"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Dùng thiết bị khác"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..145eac2
--- /dev/null
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"取消"</string>
+    <string name="string_continue" msgid="1346732695941131882">"继续"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"在另一位置创建"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"保存到另一位置"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"使用另一台设备"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"保存到其他设备"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"简单又安全的登录方式"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"借助指纹、人脸识别或屏幕锁定功能,使用不会被忘记或被盗且具有唯一性的通行密钥登录。了解详情"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"选择<xliff:g id="CREATETYPES">%1$s</xliff:g>的位置"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"保存您的密码"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"保存您的登录信息"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"在“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”中创建通行密钥?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"将您的密码保存至“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"将您的登录信息保存至“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"您可以在任意设备上使用 <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>。它会保存到“<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>”的<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"通行密钥"</string>
+    <string name="password" msgid="6738570945182936667">"密码"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"登录"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"将“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”用于您的所有登录信息?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"设为默认项"</string>
+    <string name="use_once" msgid="9027366575315399714">"使用一次"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 个密码,<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 个通行密钥"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 个密码"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 个通行密钥"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"另一台设备"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"其他密码管理工具"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"关闭工作表"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一页"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"将您已保存的通行密钥用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"将您已保存的登录信息用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"为<xliff:g id="APP_NAME">%1$s</xliff:g>选择已保存的登录信息"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"以另一种方式登录"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"不用了"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"继续"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"登录选项"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"用户:<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已锁定的密码管理工具"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"点按即可解锁"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登录信息"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"通过另一台设备"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他设备"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..f277c22
--- /dev/null
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"取消"</string>
+    <string name="string_continue" msgid="1346732695941131882">"繼續"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"在其他位置建立"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"儲存至其他位置"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"改用其他裝置"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"儲存至其他裝置"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"安全又簡便的登入方式"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"使用指紋、面孔或螢幕鎖定配合密鑰登入。密鑰獨一無二,您不用擔心忘記密鑰或密鑰被盜。瞭解詳情"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"選擇「<xliff:g id="CREATETYPES">%1$s</xliff:g>」的位置"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"儲存密碼"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"儲存登入資料"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"要在「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」建立密鑰嗎?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"要將密碼儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"要將登入資料儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"您可以在任何裝置上使用「<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>」<xliff:g id="TYPE">%2$s</xliff:g>。系統會將此資料儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>」,供「<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>」使用"</string>
+    <string name="passkey" msgid="632353688396759522">"密鑰"</string>
+    <string name="password" msgid="6738570945182936667">"密碼"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"登入資料"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"要將「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」用於所有的登入資料嗎?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"設定為預設"</string>
+    <string name="use_once" msgid="9027366575315399714">"單次使用"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼,<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密鑰"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 個密鑰"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"其他裝置"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"閂工作表"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密鑰嗎?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料嗎?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"使用其他方式登入"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"不用了,謝謝"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"繼續"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"登入選項"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 專用"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已鎖定的密碼管理工具"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"輕按即可解鎖"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登入資料"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..fd1dfc8e
--- /dev/null
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"取消"</string>
+    <string name="string_continue" msgid="1346732695941131882">"繼續"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"在其他位置建立"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"儲存至其他位置"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"改用其他裝置"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"儲存至其他裝置"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"安全又簡單的登入方式"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"登入帳戶時,你可以使用指紋、人臉或螢幕鎖定功能搭配不重複的密碼金鑰,不必擔心忘記密碼金鑰或遭人竊取。瞭解詳情"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"選擇「<xliff:g id="CREATETYPES">%1$s</xliff:g>」的位置"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"儲存密碼"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"儲存登入資訊"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"要在「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」建立密碼金鑰嗎?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"要將密碼儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"要將登入資訊儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"你可以在任何裝置上使用「<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>」<xliff:g id="TYPE">%2$s</xliff:g>。系統會將這項資料儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>」,以供「<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>」使用"</string>
+    <string name="passkey" msgid="632353688396759522">"密碼金鑰"</string>
+    <string name="password" msgid="6738570945182936667">"密碼"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"登入資訊"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"要將「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」用於所有的登入資訊嗎?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"設為預設"</string>
+    <string name="use_once" msgid="9027366575315399714">"單次使用"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼,<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密碼金鑰"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 個密碼金鑰"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"其他裝置"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"關閉功能表"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密碼金鑰嗎?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊嗎?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"使用其他方式登入"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"不用了,謝謝"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"繼續"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"登入選項"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 專用"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已鎖定的密碼管理工具"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"輕觸即可解鎖"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登入資訊"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml
new file mode 100644
index 0000000..fd2b83e
--- /dev/null
+++ b/packages/CredentialManager/res/values-zu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (4539824758261855508) -->
+    <skip />
+    <string name="string_cancel" msgid="6369133483981306063">"Khansela"</string>
+    <string name="string_continue" msgid="1346732695941131882">"Qhubeka"</string>
+    <string name="string_create_in_another_place" msgid="1033635365843437603">"Sungula kwenye indawo"</string>
+    <string name="string_save_to_another_place" msgid="7590325934591079193">"Londoloza kwenye indawo"</string>
+    <string name="string_use_another_device" msgid="8754514926121520445">"Sebenzisa enye idivayisi"</string>
+    <string name="string_save_to_another_device" msgid="1959562542075194458">"Londoloza kwenye idivayisi"</string>
+    <string name="passkey_creation_intro_title" msgid="402553911484409884">"Indlela elula yokungena ngemvume ngokuphephile"</string>
+    <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Sebenzisa isigxivizo somunwe, ubuso noma ukukhiya isikrini ukuze ungene ngemvume ngokhiye wokudlula oyingqayizivele ongenakulibaleka noma owebiwe. Funda kabanzi"</string>
+    <string name="choose_provider_title" msgid="7245243990139698508">"Khetha lapho onga-<xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+    <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+    <skip />
+    <string name="save_your_password" msgid="6597736507991704307">"Londoloza iphasiwedi yakho"</string>
+    <string name="save_your_sign_in_info" msgid="7213978049817076882">"londoloza ulwazi lwakho lokungena ngemvume"</string>
+    <!-- no translation found for choose_provider_body (8045759834416308059) -->
+    <skip />
+    <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Sungula ukhiye wokungena ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_password_title" msgid="8812546498357380545">"Londoloza ulwazi lwakho lwephasiwedi ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Londoloza ulwazi lwakho lokungena ngemvume ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+    <string name="choose_create_option_description" msgid="4419171903963100257">"Ungasebenzisa i-<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> kunoma iyiphi idivayisi. Ilondolozelwe i-<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>"</string>
+    <string name="passkey" msgid="632353688396759522">"ukhiye wokudlula"</string>
+    <string name="password" msgid="6738570945182936667">"iphasiwedi"</string>
+    <string name="sign_ins" msgid="4710739369149469208">"ukungena ngemvume"</string>
+    <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+    <skip />
+    <!-- no translation found for save_password_to_title (3450480045270186421) -->
+    <skip />
+    <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+    <skip />
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Sebenzisa i-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kukho konke ukungena kwakho ngemvume?"</string>
+    <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+    <skip />
+    <string name="set_as_default" msgid="4415328591568654603">"Setha njengokuzenzakalelayo"</string>
+    <string name="use_once" msgid="9027366575315399714">"Sebenzisa kanye"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Amaphasiwedi angu-<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, okhiye bokudlula abangu-<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Amaphasiwedi angu-<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Okhiye bokudlula abangu-<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+    <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+    <skip />
+    <string name="another_device" msgid="5147276802037801217">"Enye idivayisi"</string>
+    <string name="other_password_manager" msgid="565790221427004141">"Abanye abaphathi bephasiwedi"</string>
+    <string name="close_sheet" msgid="1393792015338908262">"Vala ishidi"</string>
+    <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Buyela emuva ekhasini langaphambilini"</string>
+    <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Sebenzisa ukhiye wakho wokungena olondoloziwe <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Sebenzisa ukungena kwakho ngemvume okulondoloziwe <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+    <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Khetha ukungena ngemvume okulondoloziwe kwakho <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Ngena ngemvume ngenye indlela"</string>
+    <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Cha ngiyabonga"</string>
+    <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Qhubeka"</string>
+    <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Okungakhethwa kukho kokungena ngemvume"</string>
+    <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Okuka-<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Abaphathi bephasiwedi abakhiyiwe"</string>
+    <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Thepha ukuze uvule"</string>
+    <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Phatha ukungena ngemvume"</string>
+    <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Kusukela kwenye idivayisi"</string>
+    <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Sebenzisa idivayisi ehlukile"</string>
+</resources>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 23953a7..e3ed3d9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -59,7 +59,7 @@
 ) {
   val requestInfo: RequestInfo
   private val providerEnabledList: List<ProviderData>
-  private val providerDisabledList: List<DisabledProviderData>
+  private val providerDisabledList: List<DisabledProviderData>?
   // TODO: require non-null.
   val resultReceiver: ResultReceiver?
 
@@ -143,7 +143,7 @@
       providerEnabledList as List<CreateCredentialProviderData>, requestDisplayInfo, context)
     val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
       // Handle runtime cast error
-      providerDisabledList as List<DisabledProviderData>, context)
+      providerDisabledList, context)
     var defaultProvider: EnabledProviderInfo? = null
     var remoteEntry: RemoteInfo? = null
     providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
@@ -209,7 +209,7 @@
     )
   }
 
-  private fun testDisabledProviderList(): List<DisabledProviderData> {
+  private fun testDisabledProviderList(): List<DisabledProviderData>? {
     return listOf(
       DisabledProviderData("com.lastpass.lpandroid"),
       DisabledProviderData("com.google.android.youtube")
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index b96f686..357c55d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -209,12 +209,12 @@
     }
 
     fun toDisabledProviderList(
-      providerDataList: List<DisabledProviderData>,
+      providerDataList: List<DisabledProviderData>?,
       context: Context,
-    ): List<com.android.credentialmanager.createflow.DisabledProviderInfo> {
+    ): List<com.android.credentialmanager.createflow.DisabledProviderInfo>? {
       // TODO: get from the actual service info
       val packageManager = context.packageManager
-      return providerDataList.map {
+      return providerDataList?.map {
         val pkgInfo = packageManager
           .getPackageInfo(it.providerFlattenedComponentName,
             PackageManager.PackageInfoFlags.of(0))
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 4757513..fe640ad 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -58,6 +58,7 @@
     privileged: true,
     platform_apis: true,
     rename_resources_package: false,
+    overrides: ["PackageInstaller"],
 
     static_libs: [
         "xz-java",
@@ -76,6 +77,7 @@
     privileged: true,
     platform_apis: true,
     rename_resources_package: false,
+    overrides: ["PackageInstaller"],
 
     static_libs: [
         "xz-java",
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
index 90e25f9..1bdba29 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
@@ -18,12 +18,12 @@
 
 import android.content.Context
 import android.net.Uri
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
 import androidx.lifecycle.Observer
 import androidx.slice.Slice
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.testutils.InstantTaskExecutorRule
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.tests.testutils.SppLayer2
diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp
index 48df569..de87dde 100644
--- a/packages/SettingsLib/Spa/testutils/Android.bp
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -24,7 +24,7 @@
     srcs: ["src/**/*.kt"],
 
     static_libs: [
-        "androidx.arch.core_core-runtime",
+        "androidx.arch.core_core-testing",
         "androidx.compose.ui_ui-test-junit4",
         "androidx.compose.ui_ui-test-manifest",
         "mockito",
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle b/packages/SettingsLib/Spa/testutils/build.gradle
index be8df43..81e54c1 100644
--- a/packages/SettingsLib/Spa/testutils/build.gradle
+++ b/packages/SettingsLib/Spa/testutils/build.gradle
@@ -47,7 +47,7 @@
 }
 
 dependencies {
-    api "androidx.arch.core:core-runtime:2.1.0"
+    api "androidx.arch.core:core-testing:2.1.0"
     api "androidx.compose.ui:ui-test-junit4:$jetpack_compose_version"
     api "com.google.truth:truth:1.1.3"
     api "org.mockito:mockito-core:2.21.0"
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/InstantTaskExecutorRule.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/InstantTaskExecutorRule.kt
deleted file mode 100644
index 43c18d4..0000000
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/InstantTaskExecutorRule.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  Copyright (C) 2022 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package com.android.settingslib.spa.testutils
-
-import androidx.arch.core.executor.ArchTaskExecutor
-import androidx.arch.core.executor.TaskExecutor
-import org.junit.rules.TestWatcher
-import org.junit.runner.Description
-
-/**
- * Test rule that makes ArchTaskExecutor main thread assertions pass. There is one such assert
- * in LifecycleRegistry.
-
- * This is a copy of androidx/arch/core/executor/testing/InstantTaskExecutorRule which should be
- * replaced once the dependency issue is solved.
- */
-class InstantTaskExecutorRule : TestWatcher() {
-    override fun starting(description: Description) {
-        super.starting(description)
-        ArchTaskExecutor.getInstance().setDelegate(
-            object : TaskExecutor() {
-                override fun executeOnDiskIO(runnable: Runnable) {
-                    runnable.run()
-                }
-
-                override fun postToMainThread(runnable: Runnable) {
-                    runnable.run()
-                }
-
-                override fun isMainThread(): Boolean {
-                    return true
-                }
-            }
-        )
-    }
-
-    override fun finished(description: Description) {
-        super.finished(description)
-        ArchTaskExecutor.getInstance().setDelegate(null)
-    }
-}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index caaa88d..b693996 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1104,7 +1104,7 @@
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
     <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string>
     <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
-    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging is paused</string>
+    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging paused</string>
     <!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
     <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging to <xliff:g id="dock_defender_threshold">%2$s</xliff:g></string>
 
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 5df79e1..e6ac48f 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -34,10 +34,7 @@
         "res",
     ],
 
-    static_libs: [
-        "PluginCoreLib",
-        "androidx.core_core-animation-nodeps",
-    ],
+    static_libs: ["androidx.core_core-animation-nodeps"],
 
     manifest: "AndroidManifest.xml",
     kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index fdfad2b..54aa351 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -75,7 +75,7 @@
      */
     interface Controller {
         /** The [ViewRootImpl] of this controller. */
-        val viewRoot: ViewRootImpl
+        val viewRoot: ViewRootImpl?
 
         /**
          * The identity object of the source animated by this controller. This animator will ensure
@@ -807,15 +807,17 @@
      * inversely, removed from the overlay when the source is moved back to its original position).
      */
     private fun synchronizeNextDraw(then: () -> Unit) {
-        if (forceDisableSynchronization) {
-            // Don't synchronize when inside an automated test.
+        val controllerRootView = controller.viewRoot?.view
+        if (forceDisableSynchronization || controllerRootView == null) {
+            // Don't synchronize when inside an automated test or if the controller root view is
+            // detached.
             then()
             return
         }
 
-        ViewRootSync.synchronizeNextDraw(controller.viewRoot.view, decorView, then)
+        ViewRootSync.synchronizeNextDraw(controllerRootView, decorView, then)
         decorView.invalidate()
-        controller.viewRoot.view.invalidate()
+        controllerRootView.invalidate()
     }
 
     private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 43bfa74..0e2d23b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -195,8 +195,16 @@
             val out = ArrayList<RemoteAnimationTarget>()
             for (i in info.changes.indices) {
                 val change = info.changes[i]
-                val changeIsWallpaper = change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0
-                if (wallpapers != changeIsWallpaper) continue
+                if (change.hasFlags(TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+                    // For embedded container, when the parent Task is also in the transition, we
+                    // should only animate the parent Task.
+                    if (change.parent != null) continue
+                    // For embedded container without parent, we should only animate if it fills
+                    // the Task. Otherwise we may animate only partial of the Task.
+                    if (!change.hasFlags(TransitionInfo.FLAG_FILLS_TASK)) continue
+                }
+                // Check if it is wallpaper
+                if (wallpapers != change.hasFlags(TransitionInfo.FLAG_IS_WALLPAPER)) continue
                 out.add(createTarget(change, info.changes.size - i, info, t))
                 if (leashMap != null) {
                     leashMap[change.leash] = out[out.size - 1].leash
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
index ecee598..964ef8c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
@@ -28,7 +28,7 @@
     private val source: View,
     override val cuj: DialogCuj?,
 ) : DialogLaunchAnimator.Controller {
-    override val viewRoot: ViewRootImpl
+    override val viewRoot: ViewRootImpl?
         get() = source.viewRootImpl
 
     override val sourceIdentity: Any = source
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
index 50c3d7e..d6db574 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
@@ -262,7 +262,7 @@
 
     private fun dialogController(cuj: DialogCuj?): DialogLaunchAnimator.Controller {
         return object : DialogLaunchAnimator.Controller {
-            override val viewRoot: ViewRootImpl = composeViewRoot.viewRootImpl
+            override val viewRoot: ViewRootImpl? = composeViewRoot.viewRootImpl
             override val sourceIdentity: Any = this@ExpandableControllerImpl
             override val cuj: DialogCuj? = cuj
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 8698844..e1f2174 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -20,7 +20,6 @@
 import android.icu.text.NumberFormat
 import android.util.TypedValue
 import android.view.LayoutInflater
-import android.view.View
 import android.widget.FrameLayout
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.customization.R
@@ -152,15 +151,9 @@
         view: AnimatableClockView,
     ) : DefaultClockFaceController(view) {
         override fun recomputePadding(targetRegion: Rect?) {
-            // We center the view within the targetRegion instead of within the parent
-            // view by computing the difference and adding that to the padding.
-            val parent = view.parent
-            val yDiff =
-                if (targetRegion != null && parent is View && parent.isLaidOut())
-                    targetRegion.centerY() - parent.height / 2f
-                else 0f
+            // Ignore Target Region until top padding fixed in aod
             val lp = view.getLayoutParams() as FrameLayout.LayoutParams
-            lp.topMargin = (-0.5f * view.bottom + yDiff).toInt()
+            lp.topMargin = (-0.5f * view.bottom).toInt()
             view.setLayoutParams(lp)
         }
 
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index a156aab..7290e7e 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -753,7 +753,7 @@
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6976786..e598afe 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -26,14 +26,11 @@
 -keep class ** extends androidx.preference.PreferenceFragment
 -keep class com.android.systemui.tuner.*
 
-# The plugins and animation subpackages both act as shared libraries that might be referenced in
+# The plugins subpackage acts as a shared library that might be referenced in
 # dynamically-loaded plugin APKs.
 -keep class com.android.systemui.plugins.** {
     *;
 }
--keep class !com.android.systemui.animation.R$**,com.android.systemui.animation.** {
-    *;
-}
 -keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
     *;
 }
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b49afee..218c5cc 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -35,6 +35,7 @@
         android:visibility="invisible" />
     <FrameLayout
         android:id="@+id/lockscreen_clock_view_large"
+        android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipChildren="false"
diff --git a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
index 669f8fb..e5e17b7 100644
--- a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
@@ -17,4 +17,7 @@
 
 <resources>
     <dimen name="widget_big_font_size">54dp</dimen>
+
+    <!-- Margin above the ambient indication container -->
+    <dimen name="ambient_indication_container_margin_top">10dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index e6593b1..6cc5b9d 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -38,6 +38,9 @@
     <!-- Minimum bottom margin under the security view -->
     <dimen name="keyguard_security_view_bottom_margin">60dp</dimen>
 
+    <!-- Margin above the ambient indication container -->
+    <dimen name="ambient_indication_container_margin_top">0dp</dimen>
+
     <dimen name="keyguard_eca_top_margin">18dp</dimen>
     <dimen name="keyguard_eca_bottom_margin">12dp</dimen>
 
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index a129fb6..da485a9 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -53,7 +53,7 @@
     <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
 
     <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that charging is temporarily limited.  -->
-    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging is paused to protect battery</string>
+    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging paused to protect battery</string>
 
     <!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock.  This is shown in small font at the bottom. -->
     <string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index 2d67d95..efcb6f3 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -14,25 +14,32 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
 -->
-<com.android.systemui.shared.shadow.DoubleShadowTextClock
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/time_view"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:fontFamily="@*android:string/config_clockFontFamily"
-    android:textColor="@android:color/white"
-    android:format12Hour="@string/dream_time_complication_12_hr_time_format"
-    android:format24Hour="@string/dream_time_complication_24_hr_time_format"
-    android:fontFeatureSettings="pnum, lnum"
-    android:letterSpacing="0.02"
-    android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
-    app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
-    app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
-    app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
-    app:keyShadowAlpha="0.3"
-    app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
-    app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
-    app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
-    app:ambientShadowAlpha="0.3"
-/>
+    android:layout_height="wrap_content">
+
+    <com.android.systemui.shared.shadow.DoubleShadowTextClock
+        android:id="@+id/time_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:fontFamily="@*android:string/config_clockFontFamily"
+        android:textColor="@android:color/white"
+        android:format12Hour="@string/dream_time_complication_12_hr_time_format"
+        android:format24Hour="@string/dream_time_complication_24_hr_time_format"
+        android:fontFeatureSettings="pnum, lnum"
+        android:letterSpacing="0.02"
+        android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
+        android:translationY="@dimen/dream_overlay_complication_clock_time_translation_y"
+        app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
+        app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
+        app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
+        app:keyShadowAlpha="0.3"
+        app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
+        app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
+        app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
+        app:ambientShadowAlpha="0.3"
+        />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
index 4f0a78e..de96e97 100644
--- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
@@ -14,16 +14,21 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
 -->
-<ImageView
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/home_controls_chip"
-    android:layout_height="@dimen/keyguard_affordance_fixed_height"
-    android:layout_width="@dimen/keyguard_affordance_fixed_width"
-    android:layout_gravity="bottom|start"
-    android:scaleType="center"
-    android:tint="?android:attr/textColorPrimary"
-    android:src="@drawable/controls_icon"
-    android:background="@drawable/keyguard_bottom_affordance_bg"
-    android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
-    android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
-    android:contentDescription="@string/quick_controls_title" />
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:paddingVertical="@dimen/dream_overlay_complication_home_controls_padding">
+
+    <ImageView
+        android:id="@+id/home_controls_chip"
+        android:layout_height="@dimen/keyguard_affordance_fixed_height"
+        android:layout_width="@dimen/keyguard_affordance_fixed_width"
+        android:layout_gravity="bottom|start"
+        android:scaleType="center"
+        android:tint="?android:attr/textColorPrimary"
+        android:src="@drawable/controls_icon"
+        android:background="@drawable/keyguard_bottom_affordance_bg"
+        android:contentDescription="@string/quick_controls_title" />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 79ba7ead..aa655e6 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -41,7 +41,7 @@
         android:layout_width="@dimen/qs_media_app_icon_size"
         android:layout_height="@dimen/qs_media_app_icon_size"
         android:layout_marginStart="@dimen/qs_media_padding"
-        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginTop="@dimen/qs_media_rec_icon_top_margin"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml
new file mode 100644
index 0000000..055308f
--- /dev/null
+++ b/packages/SystemUI/res/values-h700dp/dimens.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <!-- Margin above the ambient indication container -->
+    <dimen name="ambient_indication_container_margin_top">15dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 8efd6f0..3a71994 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -17,4 +17,7 @@
 <resources>
     <!-- With the large clock, move up slightly from the center -->
     <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
+
+    <!-- Margin above the ambient indication container -->
+    <dimen name="ambient_indication_container_margin_top">20dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 868c003..3fc59e3 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -35,6 +35,11 @@
          not appear immediately after user swipes to the side -->
     <dimen name="qs_tiles_page_horizontal_margin">20dp</dimen>
 
+    <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+    <dimen name="qs_media_rec_icon_top_margin">27dp</dimen>
+    <dimen name="qs_media_rec_album_size">152dp</dimen>
+    <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
+
     <dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
 
     <!-- Roughly the same distance as media on LS to media on QS. We will translate by this value
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8ee39dd..70d53c7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -133,6 +133,9 @@
     <color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
     <color name="biometric_dialog_error">#ffd93025</color>                  <!-- red 600 -->
 
+    <!-- SFPS colors -->
+    <color name="sfps_chevron_fill">@color/material_dynamic_primary90</color>
+
     <!-- UDFPS colors -->
     <color name="udfps_enroll_icon">#699FF3</color>
     <color name="udfps_moving_target_fill">#C2D7F7</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 569b661..46e05fe 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1055,6 +1055,7 @@
     <dimen name="qs_media_session_collapsed_guideline">144dp</dimen>
 
     <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+    <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
     <dimen name="qs_media_rec_album_size">88dp</dimen>
     <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
     <dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
@@ -1523,13 +1524,15 @@
     <dimen name="dream_overlay_status_bar_extra_margin">8dp</dimen>
 
     <!-- Dream overlay complications related dimensions -->
-    <dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
+    <dimen name="dream_overlay_complication_clock_time_text_size">86dp</dimen>
+    <dimen name="dream_overlay_complication_clock_time_translation_y">28dp</dimen>
     <dimen name="dream_overlay_complication_home_controls_padding">28dp</dimen>
     <dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
     <dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
     <dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
     <dimen name="dream_overlay_complication_shadow_padding">2dp</dimen>
     <dimen name="dream_overlay_complication_smartspace_padding">24dp</dimen>
+    <dimen name="dream_overlay_complication_smartspace_max_width">408dp</dimen>
 
     <!-- The position of the end guide, which dream overlay complications can align their start with
          if their end is aligned with the parent end. Represented as the percentage over from the
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2e5e11c..7597c62 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -200,6 +200,8 @@
 
     <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
     <string name="screenshot_saving_title">Saving screenshot\u2026</string>
+    <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
+    <string name="screenshot_saving_work_profile_title">Saving screenshot to work profile\u2026</string>
     <!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
     <string name="screenshot_saved_title">Screenshot saved</string>
     <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index f6c75a2..c9ea794 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -39,6 +39,7 @@
     private boolean mIsOrientationChanged;
     private SplitBounds mSplitBounds;
     private int mDesiredStagePosition;
+    private boolean mTaskbarInApp;
 
     public Matrix getMatrix() {
         return mMatrix;
@@ -57,6 +58,10 @@
         mDesiredStagePosition = desiredStagePosition;
     }
 
+    public void setTaskbarInApp(boolean taskbarInApp) {
+        mTaskbarInApp = taskbarInApp;
+    }
+
     /**
      * Updates the matrix based on the provided parameters
      */
@@ -83,8 +88,18 @@
                         ? mSplitBounds.topTaskPercent
                         : (1 - (mSplitBounds.topTaskPercent + mSplitBounds.dividerHeightPercent));
                 // Scale portrait height to that of (actual screen - taskbar inset)
-                fullscreenTaskHeight = (screenHeightPx - taskbarSize) * taskPercent;
-                canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+                fullscreenTaskHeight = (screenHeightPx) * taskPercent;
+                if (mTaskbarInApp) {
+                    canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+                } else {
+                    if (mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+                        // Top app isn't cropped at all by taskbar
+                        canvasScreenRatio = 0;
+                    } else {
+                        // Same as fullscreen ratio
+                        canvasScreenRatio = (float) canvasWidth / screenWidthPx;
+                    }
+                }
             } else {
                 // For landscape, scale the width
                 taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
index 3748eba..19d0a3d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
@@ -71,7 +71,7 @@
                 mKeyShadowInfo.offsetY,
                 mKeyShadowInfo.alpha
             )
-        val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN)
+        val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DST_ATOP)
         renderNode.setRenderEffect(blend)
         return renderNode
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 458d22e..a25b281 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -25,7 +25,6 @@
 
 import com.android.internal.util.EmergencyAffordanceManager;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.settingslib.Utils;
 
 /**
  * This class implements a smart emergency button that updates itself based
@@ -91,17 +90,6 @@
         return super.onTouchEvent(event);
     }
 
-    /**
-     * Reload colors from resources.
-     **/
-    public void reloadColors() {
-        int color = Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.textColorOnAccent);
-        setTextColor(color);
-        setBackground(getContext()
-                .getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background));
-    }
-
     @Override
     public boolean performLongClick() {
         return super.performLongClick();
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
index 4a41b3f..5bb9367 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
@@ -48,11 +48,13 @@
 import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
 import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
 import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
+import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
 import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
 import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
 import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
 import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
 import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
+import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
 import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
 import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
 import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
@@ -121,6 +123,9 @@
     const val FACE_AUTHENTICATED = "Face auth started/stopped because face is authenticated"
     const val BIOMETRIC_ENABLED =
         "Face auth started/stopped because biometric is enabled on keyguard"
+    const val STRONG_AUTH_ALLOWED_CHANGED = "Face auth stopped because strong auth allowed changed"
+    const val NON_STRONG_BIOMETRIC_ALLOWED_CHANGED =
+        "Face auth stopped because non strong biometric allowed changed"
 }
 
 /**
@@ -204,7 +209,11 @@
     @UiEvent(doc = FACE_AUTHENTICATED)
     FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED(1187, FACE_AUTHENTICATED),
     @UiEvent(doc = BIOMETRIC_ENABLED)
-    FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED);
+    FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED),
+    @UiEvent(doc = STRONG_AUTH_ALLOWED_CHANGED)
+    FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED(1255, STRONG_AUTH_ALLOWED_CHANGED),
+    @UiEvent(doc = NON_STRONG_BIOMETRIC_ALLOWED_CHANGED)
+    FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED(1256, NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
 
     override fun getId(): Int = this.id
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 3e32cf5..860c8e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -20,7 +20,6 @@
 import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
 import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
 
-import android.annotation.CallSuper;
 import android.content.res.ColorStateList;
 import android.os.AsyncTask;
 import android.os.CountDownTimer;
@@ -117,13 +116,6 @@
         }
     }
 
-    @CallSuper
-    @Override
-    public void reloadColors() {
-        super.reloadColors();
-        mMessageAreaController.reloadColors();
-    }
-
     @Override
     public boolean needsInput() {
         return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index faaba63..2e9ad58 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,7 +16,6 @@
 
 package com.android.keyguard;
 
-import android.annotation.CallSuper;
 import android.annotation.Nullable;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -142,16 +141,6 @@
     public void showMessage(CharSequence message, ColorStateList colorState) {
     }
 
-    /**
-     * Reload colors from resources.
-     **/
-    @CallSuper
-    public void reloadColors() {
-        if (mEmergencyButton != null) {
-            mEmergencyButton.reloadColors();
-        }
-    }
-
     public void startAppearAnimation() {
         if (TextUtils.isEmpty(mMessageAreaController.getMessage())) {
             mMessageAreaController.setMessage(getInitialMessageResId());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index e6283b8..52ca166 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -53,17 +53,17 @@
     val biometricSettingEnabledForUser: Boolean,
     val bouncerFullyShown: Boolean,
     val faceAndFpNotAuthenticated: Boolean,
+    val faceAuthAllowed: Boolean,
     val faceDisabled: Boolean,
     val faceLockedOut: Boolean,
-    val fpLockedOut: Boolean,
     val goingToSleep: Boolean,
     val keyguardAwake: Boolean,
     val keyguardGoingAway: Boolean,
     val listeningForFaceAssistant: Boolean,
     val occludingAppRequestingFaceAuth: Boolean,
     val primaryUser: Boolean,
-    val scanningAllowedByStrongAuth: Boolean,
     val secureCameraLaunched: Boolean,
+    val supportsDetect: Boolean,
     val switchingUser: Boolean,
     val udfpsBouncerShowing: Boolean,
     val udfpsFingerDown: Boolean,
@@ -79,9 +79,8 @@
     // keep sorted
     val awakeKeyguard: Boolean,
     val authInterruptActive: Boolean,
-    val encryptedOrTimedOut: Boolean,
-    val fpLockout: Boolean,
-    val lockDown: Boolean,
+    val fpLockedOut: Boolean,
+    val primaryAuthRequired: Boolean,
     val switchingUser: Boolean,
     val triggerActiveUnlockForAssistant: Boolean,
     val userCanDismissLockScreen: Boolean
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index c29f632..6a92162 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -116,13 +116,6 @@
         return mView.getText();
     }
 
-    /**
-     * Reload colors from resources.
-     **/
-    public void reloadColors() {
-        mView.reloadColor();
-    }
-
     /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
     public static class Factory {
         private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0025986..195e8f9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -16,7 +16,6 @@
 
 package com.android.keyguard;
 
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.os.UserHandle;
 import android.text.Editable;
@@ -39,7 +38,6 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -95,18 +93,6 @@
         }
     };
 
-    @Override
-    public void reloadColors() {
-        super.reloadColors();
-        int textColor = Utils.getColorAttr(mView.getContext(),
-                android.R.attr.textColorPrimary).getDefaultColor();
-        mPasswordEntry.setTextColor(textColor);
-        mPasswordEntry.setHighlightColor(textColor);
-        mPasswordEntry.setBackgroundTintList(ColorStateList.valueOf(textColor));
-        mPasswordEntry.setForegroundTintList(ColorStateList.valueOf(textColor));
-        mSwitchImeButton.setImageTintList(ColorStateList.valueOf(textColor));
-    }
-
     protected KeyguardPasswordViewController(KeyguardPasswordView view,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecurityMode securityMode,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index cdbfb24..571d274 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -35,7 +35,6 @@
 import com.android.internal.widget.LockscreenCredential;
 import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingClassifier;
 import com.android.systemui.classifier.FalsingCollector;
@@ -272,16 +271,6 @@
     }
 
     @Override
-    public void reloadColors() {
-        super.reloadColors();
-        mMessageAreaController.reloadColors();
-        int textColor = Utils.getColorAttr(mLockPatternView.getContext(),
-                android.R.attr.textColorSecondary).getDefaultColor();
-        int errorColor = Utils.getColorError(mLockPatternView.getContext()).getDefaultColor();
-        mLockPatternView.setColors(textColor, textColor, errorColor);
-    }
-
-    @Override
     public void onPause() {
         super.onPause();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 7876f07..f51ac32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -70,12 +70,6 @@
     }
 
     @Override
-    public void reloadColors() {
-        super.reloadColors();
-        mView.reloadColors();
-    }
-
-    @Override
     public boolean startDisappearAnimation(Runnable finishRunnable) {
         return mView.startDisappearAnimation(
                 mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 4d0a273..a72a484 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -730,16 +730,20 @@
     }
 
     private void reloadColors() {
-        mSecurityViewFlipperController.reloadColors();
+        resetViewFlipper();
         mView.reloadColors();
     }
 
     /** Handles density or font scale changes. */
     private void onDensityOrFontScaleChanged() {
-        mSecurityViewFlipperController.onDensityOrFontScaleChanged();
+        resetViewFlipper();
+        mView.onDensityOrFontScaleChanged();
+    }
+
+    private void resetViewFlipper() {
+        mSecurityViewFlipperController.clearViews();
         mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
                 mKeyguardSecurityCallback);
-        mView.onDensityOrFontScaleChanged();
     }
 
     static class Factory {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 25afe11..a5c8c78 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -74,17 +74,8 @@
         }
     }
 
-    /**
-     * Reload colors of ui elements upon theme change.
-     */
-    public void reloadColors() {
-        for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
-            child.reloadColors();
-        }
-    }
-
     /** Handles density or font scale changes. */
-    public void onDensityOrFontScaleChanged() {
+    public void clearViews() {
         mView.removeAllViews();
         mChildren.clear();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 91bf20f..a16f3047 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -133,12 +133,6 @@
     }
 
     @Override
-    public void reloadColors() {
-        super.reloadColors();
-        mView.reloadColors();
-    }
-
-    @Override
     protected void verifyPasswordAndUnlock() {
         String entry = mPasswordEntry.getText();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 5995e85..e9405eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -121,12 +121,6 @@
     }
 
     @Override
-    public void reloadColors() {
-        super.reloadColors();
-        mView.reloadColors();
-    }
-
-    @Override
     protected void verifyPasswordAndUnlock() {
         mStateMachine.next();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 993d80f..479d454 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -34,9 +34,9 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.keyguard.FaceAuthReasonKt.apiRequestReasonToUiEvent;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_DREAM_STARTED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP;
@@ -65,6 +65,7 @@
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
@@ -170,7 +171,6 @@
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
@@ -735,8 +735,8 @@
      */
     public void requestFaceAuthOnOccludingApp(boolean request) {
         mOccludingAppRequestingFace = request;
-        updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
+        int action = mOccludingAppRequestingFace ? BIOMETRIC_ACTION_UPDATE : BIOMETRIC_ACTION_STOP;
+        updateFaceListeningState(action, FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
     }
 
     /**
@@ -1378,16 +1378,12 @@
                 && !mFingerprintLockedOut;
     }
 
-    private boolean isUnlockingWithFaceAllowed() {
-        return mStrongAuthTracker.isUnlockingWithBiometricAllowed(false);
-    }
-
     /**
      * Whether fingerprint is allowed ot be used for unlocking based on the strongAuthTracker
      * and temporary lockout state (tracked by FingerprintManager via error codes).
      */
     public boolean isUnlockingWithFingerprintAllowed() {
-        return isUnlockingWithBiometricAllowed(true);
+        return isUnlockingWithBiometricAllowed(FINGERPRINT);
     }
 
     /**
@@ -1397,14 +1393,17 @@
             @NonNull BiometricSourceType biometricSourceType) {
         switch (biometricSourceType) {
             case FINGERPRINT:
-                return isUnlockingWithFingerprintAllowed();
+                return isUnlockingWithBiometricAllowed(true);
             case FACE:
-                return isUnlockingWithFaceAllowed();
+                return isUnlockingWithBiometricAllowed(false);
             default:
                 return false;
         }
     }
 
+    /**
+     * Whether the user locked down the device. This doesn't include device policy manager lockdown.
+     */
     public boolean isUserInLockdown(int userId) {
         return containsFlag(mStrongAuthTracker.getStrongAuthForUser(userId),
                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
@@ -1436,7 +1435,8 @@
         return mStrongAuthTracker;
     }
 
-    private void notifyStrongAuthStateChanged(int userId) {
+    @VisibleForTesting
+    void notifyStrongAuthAllowedChanged(int userId) {
         Assert.isMainThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1444,6 +1444,15 @@
                 cb.onStrongAuthStateChanged(userId);
             }
         }
+        if (userId == getCurrentUser()) {
+            FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo(
+                    mStrongAuthTracker.getStrongAuthForUser(getCurrentUser()));
+
+            // Strong auth is only reset when primary auth is used to enter the device,
+            // so we only check whether to stop biometric listening states here
+            updateBiometricListeningState(
+                    BIOMETRIC_ACTION_STOP, FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED);
+        }
     }
 
     private void notifyLockedOutStateChanged(BiometricSourceType type) {
@@ -1455,8 +1464,8 @@
             }
         }
     }
-
-    private void notifyNonStrongBiometricStateChanged(int userId) {
+    @VisibleForTesting
+    void notifyNonStrongBiometricAllowedChanged(int userId) {
         Assert.isMainThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1464,6 +1473,16 @@
                 cb.onNonStrongBiometricAllowedChanged(userId);
             }
         }
+        if (userId == getCurrentUser()) {
+            FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo(
+                    mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(
+                            getCurrentUser()) ? -1 : 1);
+
+            // This is only reset when primary auth is used to enter the device, so we only check
+            // whether to stop biometric listening states here
+            updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
+                    FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
+        }
     }
 
     private void dispatchErrorMessage(CharSequence message) {
@@ -1809,16 +1828,12 @@
         }
     }
 
-    public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
-        private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
-        private final Consumer<Integer> mNonStrongBiometricAllowedChanged;
-
-        public StrongAuthTracker(Context context,
-                Consumer<Integer> strongAuthRequiredChangedCallback,
-                Consumer<Integer> nonStrongBiometricAllowedChanged) {
+    /**
+     * Updates callbacks when strong auth requirements change.
+     */
+    public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+        public StrongAuthTracker(Context context) {
             super(context);
-            mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
-            mNonStrongBiometricAllowedChanged = nonStrongBiometricAllowedChanged;
         }
 
         public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
@@ -1834,7 +1849,7 @@
 
         @Override
         public void onStrongAuthRequiredChanged(int userId) {
-            mStrongAuthRequiredChangedCallback.accept(userId);
+            notifyStrongAuthAllowedChanged(userId);
         }
 
         // TODO(b/247091681): Renaming the inappropriate onIsNonStrongBiometricAllowedChanged
@@ -1842,7 +1857,7 @@
         //  Strong-Auth
         @Override
         public void onIsNonStrongBiometricAllowedChanged(int userId) {
-            mNonStrongBiometricAllowedChanged.accept(userId);
+            notifyNonStrongBiometricAllowedChanged(userId);
         }
     }
 
@@ -2003,8 +2018,7 @@
         mUserTracker = userTracker;
         mTelephonyListenerManager = telephonyListenerManager;
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
-        mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged,
-                this::notifyNonStrongBiometricStateChanged);
+        mStrongAuthTracker = new StrongAuthTracker(context);
         mBackgroundExecutor = backgroundExecutor;
         mBroadcastDispatcher = broadcastDispatcher;
         mInteractionJankMonitor = interactionJankMonitor;
@@ -2579,24 +2593,17 @@
                 || !mLockPatternUtils.isSecure(user);
 
         // Don't trigger active unlock if fp is locked out
-        final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+        final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
 
         // Don't trigger active unlock if primary auth is required
-        final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
-        final boolean isLockDown =
-                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
-                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
-        final boolean isEncryptedOrTimedOut =
-                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
-                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+        final boolean primaryAuthRequired = !isUnlockingWithBiometricAllowed(true);
 
         final boolean shouldTriggerActiveUnlock =
                 (mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard)
                         && !mSwitchingUser
                         && !userCanDismissLockScreen
-                        && !fpLockedout
-                        && !isLockDown
-                        && !isEncryptedOrTimedOut
+                        && !fpLockedOut
+                        && !primaryAuthRequired
                         && !mKeyguardGoingAway
                         && !mSecureCameraLaunched;
 
@@ -2608,9 +2615,8 @@
                         shouldTriggerActiveUnlock,
                         awakeKeyguard,
                         mAuthInterruptActive,
-                        isEncryptedOrTimedOut,
-                        fpLockedout,
-                        isLockDown,
+                        fpLockedOut,
+                        primaryAuthRequired,
                         mSwitchingUser,
                         triggerActiveUnlockForAssistant,
                         userCanDismissLockScreen));
@@ -2662,7 +2668,8 @@
                         && !fingerprintDisabledForUser
                         && (!mKeyguardGoingAway || !mDeviceInteractive)
                         && mIsPrimaryUser
-                        && biometricEnabledForUser;
+                        && biometricEnabledForUser
+                        && !isUserInLockdown(user);
         final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
         final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
         final boolean shouldListenBouncerState =
@@ -2724,14 +2731,7 @@
         final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive
                 && !statusBarShadeLocked;
         final int user = getCurrentUser();
-        final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
-        final boolean isLockDown =
-                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
-                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
-        final boolean isEncryptedOrTimedOut =
-                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
-                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
-        final boolean fpLockedOut = isFingerprintLockedOut();
+        final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE);
         final boolean canBypass = mKeyguardBypassController != null
                 && mKeyguardBypassController.canBypass();
         // There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2739,20 +2739,15 @@
         // the lock screen even when TrustAgents are keeping the device unlocked.
         final boolean userNotTrustedOrDetectionIsNeeded = !getUserHasTrust(user) || canBypass;
 
-        // Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
-        // Lock-down mode shouldn't scan, since it is more explicit.
-        boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass
-                && !mPrimaryBouncerFullyShown);
-
-        // If the device supports face detection (without authentication) and bypass is enabled,
-        // allow face scanning to happen if the device is in lockdown mode.
-        // Otherwise, prevent scanning.
-        final boolean supportsDetectOnly = !mFaceSensorProperties.isEmpty()
-                && canBypass
-                && mFaceSensorProperties.get(0).supportsFaceDetection;
-        if (isLockDown && !supportsDetectOnly) {
-            strongAuthAllowsScanning = false;
-        }
+        // If the device supports face detection (without authentication), if bypass is enabled,
+        // allow face detection to happen even if stronger auth is required. When face is detected,
+        // we show the bouncer. However, if the user manually locked down the device themselves,
+        // never attempt to detect face.
+        final boolean supportsDetect = !mFaceSensorProperties.isEmpty()
+                && mFaceSensorProperties.get(0).supportsFaceDetection
+                && canBypass && !mPrimaryBouncerIsOrWillBeShowing
+                && !isUserInLockdown(user);
+        final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect;
 
         // If the face or fp has recently been authenticated do not attempt to authenticate again.
         final boolean faceAndFpNotAuthenticated = !getUserUnlockedWithBiometric(user);
@@ -2773,14 +2768,10 @@
                         || mUdfpsBouncerShowing)
                 && !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
                 && !mKeyguardGoingAway && biometricEnabledForUser
-                && strongAuthAllowsScanning && mIsPrimaryUser
+                && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
                 && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
                 && faceAndFpNotAuthenticated
-                && !mGoingToSleep
-                // We only care about fp locked out state and not face because we still trigger
-                // face auth even when face is locked out to show the user a message that face
-                // unlock was supposed to run but didn't
-                && !fpLockedOut;
+                && !mGoingToSleep;
 
         // Aggregate relevant fields for debug logging.
         maybeLogListenerModelData(
@@ -2790,19 +2781,19 @@
                     shouldListen,
                     mAuthInterruptActive,
                     biometricEnabledForUser,
-                        mPrimaryBouncerFullyShown,
+                    mPrimaryBouncerFullyShown,
                     faceAndFpNotAuthenticated,
+                    faceAuthAllowed,
                     faceDisabledForUser,
                     isFaceLockedOut(),
-                    fpLockedOut,
                     mGoingToSleep,
                     awakeKeyguard,
                     mKeyguardGoingAway,
                     shouldListenForFaceAssistant,
                     mOccludingAppRequestingFace,
                     mIsPrimaryUser,
-                    strongAuthAllowsScanning,
                     mSecureCameraLaunched,
+                    supportsDetect,
                     mSwitchingUser,
                     mUdfpsBouncerShowing,
                     isUdfpsFingerDown,
@@ -2906,9 +2897,11 @@
             // This would need to be updated for multi-sensor devices
             final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
                     && mFaceSensorProperties.get(0).supportsFaceDetection;
-            if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
+            if (!isUnlockingWithBiometricAllowed(FACE) && supportsFaceDetection) {
+                mLogger.v("startListeningForFace - detect");
                 mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
             } else {
+                mLogger.v("startListeningForFace - authenticate");
                 final boolean isBypassEnabled = mKeyguardBypassController != null
                         && mKeyguardBypassController.isBypassEnabled();
                 mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 929ebea..becf5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -39,10 +39,13 @@
  */
 public interface CoreStartable extends Dumpable {
 
-    /** Main entry point for implementations. Called shortly after app startup. */
+    /** Main entry point for implementations. Called shortly after SysUI startup. */
     void start();
 
-    /** */
+    /** Called when the device configuration changes. This will not be called before
+     * {@link #start()}, but it could be called before {@link #onBootCompleted()}.
+     *
+     * @see android.app.Application#onConfigurationChanged(Configuration)  */
     default void onConfigurationChanged(Configuration newConfig) {
     }
 
@@ -50,7 +53,11 @@
     default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
     }
 
-    /** Called when the device reports BOOT_COMPLETED. */
+    /** Called immediately after the system broadcasts
+     * {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED} or during SysUI startup if the
+     * property {@code sys.boot_completed} is already set to 1. The latter typically occurs when
+     * starting a new SysUI instance, such as when starting SysUI for a secondary user.
+     * {@link #onBootCompleted()} will never be called before {@link #start()}. */
     default void onBootCompleted() {
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 9a8d532..104b71f 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -17,24 +17,25 @@
 package com.android.systemui;
 
 import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
-import android.util.Log;
+
+import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.qs.QSUserSwitcherEvent;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.util.settings.SecureSettings;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 import dagger.assisted.Assisted;
@@ -44,31 +45,66 @@
 /**
  * Manages notification when a guest session is resumed.
  */
-public class GuestResumeSessionReceiver extends BroadcastReceiver {
-
-    private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName();
+public class GuestResumeSessionReceiver {
 
     @VisibleForTesting
     public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
 
     @VisibleForTesting
     public AlertDialog mNewSessionDialog;
+    private final Executor mMainExecutor;
     private final UserTracker mUserTracker;
     private final SecureSettings mSecureSettings;
-    private final BroadcastDispatcher mBroadcastDispatcher;
     private final ResetSessionDialog.Factory mResetSessionDialogFactory;
     private final GuestSessionNotification mGuestSessionNotification;
 
+    @VisibleForTesting
+    public final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    cancelDialog();
+
+                    UserInfo currentUser = mUserTracker.getUserInfo();
+                    if (!currentUser.isGuest()) {
+                        return;
+                    }
+
+                    int guestLoginState = mSecureSettings.getIntForUser(
+                            SETTING_GUEST_HAS_LOGGED_IN, 0, newUser);
+
+                    if (guestLoginState == 0) {
+                        // set 1 to indicate, 1st login
+                        guestLoginState = 1;
+                        mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState,
+                                newUser);
+                    } else if (guestLoginState == 1) {
+                        // set 2 to indicate, 2nd or later login
+                        guestLoginState = 2;
+                        mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState,
+                                newUser);
+                    }
+
+                    mGuestSessionNotification.createPersistentNotification(currentUser,
+                            (guestLoginState <= 1));
+
+                    if (guestLoginState > 1) {
+                        mNewSessionDialog = mResetSessionDialogFactory.create(newUser);
+                        mNewSessionDialog.show();
+                    }
+                }
+            };
+
     @Inject
     public GuestResumeSessionReceiver(
+            @Main Executor mainExecutor,
             UserTracker userTracker,
             SecureSettings secureSettings,
-            BroadcastDispatcher broadcastDispatcher,
             GuestSessionNotification guestSessionNotification,
             ResetSessionDialog.Factory resetSessionDialogFactory) {
+        mMainExecutor = mainExecutor;
         mUserTracker = userTracker;
         mSecureSettings = secureSettings;
-        mBroadcastDispatcher = broadcastDispatcher;
         mGuestSessionNotification = guestSessionNotification;
         mResetSessionDialogFactory = resetSessionDialogFactory;
     }
@@ -77,49 +113,7 @@
      * Register this receiver with the {@link BroadcastDispatcher}
      */
     public void register() {
-        IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        String action = intent.getAction();
-
-        if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-            cancelDialog();
-
-            int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-            if (userId == UserHandle.USER_NULL) {
-                Log.e(TAG, intent + " sent to " + TAG + " without EXTRA_USER_HANDLE");
-                return;
-            }
-
-            UserInfo currentUser = mUserTracker.getUserInfo();
-            if (!currentUser.isGuest()) {
-                return;
-            }
-
-            int guestLoginState = mSecureSettings.getIntForUser(
-                    SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
-
-            if (guestLoginState == 0) {
-                // set 1 to indicate, 1st login
-                guestLoginState = 1;
-                mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
-            } else if (guestLoginState == 1) {
-                // set 2 to indicate, 2nd or later login
-                guestLoginState = 2;
-                mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
-            }
-
-            mGuestSessionNotification.createPersistentNotification(currentUser,
-                                                                   (guestLoginState <= 1));
-
-            if (guestLoginState > 1) {
-                mNewSessionDialog = mResetSessionDialogFactory.create(userId);
-                mNewSessionDialog.show();
-            }
-        }
+        mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
     }
 
     private void cancelDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 7e3b1389..02a6d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -26,10 +26,7 @@
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -45,7 +42,6 @@
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.util.DisplayUtils;
 import android.util.Log;
@@ -68,7 +64,6 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.Utils;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.decor.CutoutDecorProviderFactory;
@@ -128,7 +123,6 @@
     private DisplayManager mDisplayManager;
     @VisibleForTesting
     protected boolean mIsRegistered;
-    private final BroadcastDispatcher mBroadcastDispatcher;
     private final Context mContext;
     private final Executor mMainExecutor;
     private final TunerService mTunerService;
@@ -302,7 +296,6 @@
     public ScreenDecorations(Context context,
             @Main Executor mainExecutor,
             SecureSettings secureSettings,
-            BroadcastDispatcher broadcastDispatcher,
             TunerService tunerService,
             UserTracker userTracker,
             PrivacyDotViewController dotViewController,
@@ -312,7 +305,6 @@
         mContext = context;
         mMainExecutor = mainExecutor;
         mSecureSettings = secureSettings;
-        mBroadcastDispatcher = broadcastDispatcher;
         mTunerService = tunerService;
         mUserTracker = userTracker;
         mDotViewController = dotViewController;
@@ -598,10 +590,7 @@
             mColorInversionSetting.onChange(false);
             updateColorInversion(mColorInversionSetting.getValue());
 
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
-            mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
-                    mExecutor, UserHandle.ALL);
+            mUserTracker.addCallback(mUserChangedCallback, mExecutor);
             mIsRegistered = true;
         } else {
             mMainExecutor.execute(() -> mTunerService.removeTunable(this));
@@ -610,7 +599,7 @@
                 mColorInversionSetting.setListening(false);
             }
 
-            mBroadcastDispatcher.unregisterReceiver(mUserSwitchIntentReceiver);
+            mUserTracker.removeCallback(mUserChangedCallback);
             mIsRegistered = false;
         }
     }
@@ -897,18 +886,18 @@
         }
     }
 
-    private final BroadcastReceiver mUserSwitchIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            int newUserId = mUserTracker.getUserId();
-            if (DEBUG) {
-                Log.d(TAG, "UserSwitched newUserId=" + newUserId);
-            }
-            // update color inversion setting to the new user
-            mColorInversionSetting.setUserId(newUserId);
-            updateColorInversion(mColorInversionSetting.getValue());
-        }
-    };
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    if (DEBUG) {
+                        Log.d(TAG, "UserSwitched newUserId=" + newUser);
+                    }
+                    // update color inversion setting to the new user
+                    mColorInversionSetting.setUserId(newUser);
+                    updateColorInversion(mColorInversionSetting.getValue());
+                }
+            };
 
     private void updateColorInversion(int colorsInvertedValue) {
         mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a7519cf..db2239b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -623,6 +623,10 @@
                     getFingerprintSensorLocationInNaturalOrientation(),
                     mCachedDisplayInfo);
         }
+
+        for (final Callback cb : mCallbacks) {
+            cb.onFingerprintLocationChanged();
+        }
     }
 
     /**
@@ -644,6 +648,10 @@
                     mCachedDisplayInfo
             );
         }
+
+        for (final Callback cb : mCallbacks) {
+            cb.onFaceSensorLocationChanged();
+        }
     }
 
     /**
@@ -1325,8 +1333,24 @@
         default void onBiometricPromptDismissed() {}
 
         /**
-         * The location in pixels can change due to resolution changes.
+         * Called when the location of the fingerprint sensor changes. The location in pixels can
+         * change due to resolution changes.
+         */
+        default void onFingerprintLocationChanged() {}
+
+        /**
+         * Called when the location of the under display fingerprint sensor changes. The location in
+         * pixels can change due to resolution changes.
+         *
+         * On devices with UDFPS, this is always called alongside
+         * {@link #onFingerprintLocationChanged}.
          */
         default void onUdfpsLocationChanged() {}
+
+        /**
+         * Called when the location of the face unlock sensor (typically the front facing camera)
+         * changes. The location in pixels can change due to resolution changes.
+         */
+        default void onFaceSensorLocationChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6ac54fe..d561cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -29,6 +29,8 @@
 import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.CircleReveal
@@ -71,7 +73,8 @@
     private val biometricUnlockController: BiometricUnlockController,
     private val udfpsControllerProvider: Provider<UdfpsController>,
     private val statusBarStateController: StatusBarStateController,
-    rippleView: AuthRippleView?
+    private val featureFlags: FeatureFlags,
+        rippleView: AuthRippleView?
 ) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
     WakefulnessLifecycle.Observer {
 
@@ -159,12 +162,17 @@
 
     private fun showUnlockedRipple() {
         notificationShadeWindowController.setForcePluginOpen(true, this)
-        val lightRevealScrim = centralSurfaces.lightRevealScrim
-        if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
-            circleReveal?.let {
-                lightRevealScrim?.revealAmount = 0f
-                lightRevealScrim?.revealEffect = it
-                startLightRevealScrimOnKeyguardFadingAway = true
+
+        // This code path is not used if the KeyguardTransitionRepository is managing the light
+        // reveal scrim.
+        if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+            val lightRevealScrim = centralSurfaces.lightRevealScrim
+            if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+                circleReveal?.let {
+                    lightRevealScrim?.revealAmount = 0f
+                    lightRevealScrim?.revealEffect = it
+                    startLightRevealScrimOnKeyguardFadingAway = true
+                }
             }
         }
 
@@ -177,6 +185,10 @@
     }
 
     override fun onKeyguardFadingAwayChanged() {
+        if (featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+            return
+        }
+
         if (keyguardStateController.isKeyguardFadingAway) {
             val lightRevealScrim = centralSurfaces.lightRevealScrim
             if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 1c3dd45..17ebdad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -135,7 +135,7 @@
         WindowManager.LayoutParams(
                 WindowManager.LayoutParams.WRAP_CONTENT,
                 WindowManager.LayoutParams.WRAP_CONTENT,
-                WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
                 Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
                 PixelFormat.TRANSLUCENT
             )
@@ -370,11 +370,15 @@
 private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
     fun update() {
         val c = context.getColor(R.color.biometric_dialog_accent)
+        val chevronFill = context.getColor(R.color.sfps_chevron_fill)
         for (key in listOf(".blue600", ".blue400")) {
             addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
                 PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
             }
         }
+        addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+            PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
+        }
     }
 
     if (composition != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 1d4281f..19b0548 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -62,6 +62,11 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.biometrics.dagger.BiometricsBackground;
+import com.android.systemui.biometrics.udfps.InteractionEvent;
+import com.android.systemui.biometrics.udfps.NormalizedTouchData;
+import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessorResult;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
@@ -143,6 +148,7 @@
     @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
     @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
     @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+    @Nullable private final TouchProcessor mTouchProcessor;
 
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
@@ -166,7 +172,6 @@
 
     // The current request from FingerprintService. Null if no current request.
     @Nullable UdfpsControllerOverlay mOverlay;
-    @Nullable private UdfpsEllipseDetection mUdfpsEllipseDetection;
 
     // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
     // to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -355,10 +360,6 @@
         if (!mOverlayParams.equals(overlayParams)) {
             mOverlayParams = overlayParams;
 
-            if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
-                mUdfpsEllipseDetection.updateOverlayParams(overlayParams);
-            }
-
             final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer();
 
             // When the bounds change it's always necessary to re-create the overlay's window with
@@ -467,8 +468,99 @@
         return portraitTouch;
     }
 
+    private void tryDismissingKeyguard() {
+        if (!mOnFingerDown) {
+            playStartHaptic();
+        }
+        mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
+        mAttemptedToDismissKeyguard = true;
+    }
+
     @VisibleForTesting
     boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+        if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+            return newOnTouch(requestId, event, fromUdfpsView);
+        } else {
+            return oldOnTouch(requestId, event, fromUdfpsView);
+        }
+    }
+
+    private boolean newOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+        if (!fromUdfpsView) {
+            Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
+            return false;
+        }
+        if (mOverlay == null) {
+            Log.w(TAG, "ignoring onTouch with null overlay");
+            return false;
+        }
+        if (!mOverlay.matchesRequestId(requestId)) {
+            Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
+                    + mOverlay.getRequestId());
+            return false;
+        }
+
+        final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
+                mOverlayParams);
+        if (result instanceof TouchProcessorResult.Failure) {
+            Log.w(TAG, ((TouchProcessorResult.Failure) result).getReason());
+            return false;
+        }
+
+        final TouchProcessorResult.ProcessedTouch processedTouch =
+                (TouchProcessorResult.ProcessedTouch) result;
+        final NormalizedTouchData data = processedTouch.getTouchData();
+
+        mActivePointerId = processedTouch.getPointerOnSensorId();
+        switch (processedTouch.getEvent()) {
+            case DOWN:
+                if (shouldTryToDismissKeyguard()) {
+                    tryDismissingKeyguard();
+                }
+                onFingerDown(requestId,
+                        data.getPointerId(),
+                        data.getX(),
+                        data.getY(),
+                        data.getMinor(),
+                        data.getMajor(),
+                        data.getOrientation(),
+                        data.getTime(),
+                        data.getGestureStart(),
+                        mStatusBarStateController.isDozing());
+                break;
+
+            case UP:
+            case CANCEL:
+                if (InteractionEvent.CANCEL.equals(processedTouch.getEvent())) {
+                    Log.w(TAG, "This is a CANCEL event that's reported as an UP event!");
+                }
+                mAttemptedToDismissKeyguard = false;
+                onFingerUp(requestId,
+                        mOverlay.getOverlayView(),
+                        data.getPointerId(),
+                        data.getX(),
+                        data.getY(),
+                        data.getMinor(),
+                        data.getMajor(),
+                        data.getOrientation(),
+                        data.getTime(),
+                        data.getGestureStart(),
+                        mStatusBarStateController.isDozing());
+                mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
+                break;
+
+
+            default:
+                break;
+        }
+
+        // We should only consume touches that are within the sensor. By returning "false" for
+        // touches outside of the sensor, we let other UI components consume these events and act on
+        // them appropriately.
+        return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds());
+    }
+
+    private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
         if (mOverlay == null) {
             Log.w(TAG, "ignoring onTouch with null overlay");
             return false;
@@ -498,23 +590,8 @@
                     mVelocityTracker.clear();
                 }
 
-                boolean withinSensorArea;
-                if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
-                    if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
-                        // Ellipse detection
-                        withinSensorArea = mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
-                    } else {
-                        // Centroid with expanded overlay
-                        withinSensorArea =
-                            isWithinSensorArea(udfpsView, event.getRawX(),
-                                        event.getRawY(), fromUdfpsView);
-                    }
-                } else {
-                    // Centroid with sensor sized view
-                    withinSensorArea =
+                final boolean withinSensorArea =
                         isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
-                }
-
                 if (withinSensorArea) {
                     Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
                     Log.v(TAG, "onTouch | action down");
@@ -528,11 +605,7 @@
                 }
                 if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) {
                     Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN");
-                    if (!mOnFingerDown) {
-                        playStartHaptic();
-                    }
-                    mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
-                    mAttemptedToDismissKeyguard = true;
+                    tryDismissingKeyguard();
                 }
 
                 Trace.endSection();
@@ -545,33 +618,13 @@
                         ? event.getPointerId(0)
                         : event.findPointerIndex(mActivePointerId);
                 if (idx == event.getActionIndex()) {
-                    boolean actionMoveWithinSensorArea;
-                    if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
-                        if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
-                            // Ellipse detection
-                            actionMoveWithinSensorArea =
-                                    mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
-                        } else {
-                            // Centroid with expanded overlay
-                            actionMoveWithinSensorArea =
-                                isWithinSensorArea(udfpsView, event.getRawX(idx),
-                                        event.getRawY(idx), fromUdfpsView);
-                        }
-                    } else {
-                        // Centroid with sensor sized view
-                        actionMoveWithinSensorArea =
-                            isWithinSensorArea(udfpsView, event.getX(idx),
-                                    event.getY(idx), fromUdfpsView);
-                    }
-
+                    final boolean actionMoveWithinSensorArea =
+                            isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx),
+                                    fromUdfpsView);
                     if ((fromUdfpsView || actionMoveWithinSensorArea)
                             && shouldTryToDismissKeyguard()) {
                         Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
-                        if (!mOnFingerDown) {
-                            playStartHaptic();
-                        }
-                        mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
-                        mAttemptedToDismissKeyguard = true;
+                        tryDismissingKeyguard();
                         break;
                     }
                     // Map the touch to portrait mode if the device is in landscape mode.
@@ -696,7 +749,8 @@
             @NonNull ActivityLaunchAnimator activityLaunchAnimator,
             @NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
             @NonNull @BiometricsBackground Executor biometricsExecutor,
-            @NonNull PrimaryBouncerInteractor primaryBouncerInteractor) {
+            @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
+            @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor) {
         mContext = context;
         mExecution = execution;
         mVibrator = vibrator;
@@ -737,6 +791,9 @@
         mBiometricExecutor = biometricsExecutor;
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
 
+        mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
+                ? singlePointerTouchProcessor : null;
+
         mDumpManager.registerDumpable(TAG, this);
 
         mOrientationListener = new BiometricDisplayListener(
@@ -761,10 +818,6 @@
 
         udfpsHapticsSimulator.setUdfpsController(this);
         udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);
-
-        if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
-            mUdfpsEllipseDetection = new UdfpsEllipseDetection(mOverlayParams);
-        }
     }
 
     /**
@@ -946,7 +999,36 @@
         return mOnFingerDown;
     }
 
-    private void onFingerDown(long requestId, int x, int y, float minor, float major) {
+    private void onFingerDown(
+            long requestId,
+            int x,
+            int y,
+            float minor,
+            float major) {
+        onFingerDown(
+                requestId,
+                MotionEvent.INVALID_POINTER_ID /* pointerId */,
+                x,
+                y,
+                minor,
+                major,
+                0f /* orientation */,
+                0L /* time */,
+                0L /* gestureStart */,
+                false /* isAod */);
+    }
+
+    private void onFingerDown(
+            long requestId,
+            int pointerId,
+            float x,
+            float y,
+            float minor,
+            float major,
+            float orientation,
+            long time,
+            long gestureStart,
+            boolean isAod) {
         mExecution.assertIsMainThread();
 
         if (mOverlay == null) {
@@ -975,7 +1057,7 @@
         mOnFingerDown = true;
         if (mAlternateTouchProvider != null) {
             mBiometricExecutor.execute(() -> {
-                mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
+                mAlternateTouchProvider.onPointerDown(requestId, (int) x, (int) y, minor, major);
             });
             mFgExecutor.execute(() -> {
                 if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
@@ -983,7 +1065,13 @@
                 }
             });
         } else {
-            mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
+            if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+                mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
+                        minor, major, orientation, time, gestureStart, isAod);
+            } else {
+                mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, (int) x,
+                        (int) y, minor, major);
+            }
         }
         Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
         final UdfpsView view = mOverlay.getOverlayView();
@@ -1007,6 +1095,32 @@
     }
 
     private void onFingerUp(long requestId, @NonNull UdfpsView view) {
+        onFingerUp(
+                requestId,
+                view,
+                MotionEvent.INVALID_POINTER_ID /* pointerId */,
+                0f /* x */,
+                0f /* y */,
+                0f /* minor */,
+                0f /* major */,
+                0f /* orientation */,
+                0L /* time */,
+                0L /* gestureStart */,
+                false /* isAod */);
+    }
+
+    private void onFingerUp(
+            long requestId,
+            @NonNull UdfpsView view,
+            int pointerId,
+            float x,
+            float y,
+            float minor,
+            float major,
+            float orientation,
+            long time,
+            long gestureStart,
+            boolean isAod) {
         mExecution.assertIsMainThread();
         mActivePointerId = -1;
         mAcquiredReceived = false;
@@ -1021,7 +1135,12 @@
                     }
                 });
             } else {
-                mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+                if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+                    mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
+                            y, minor, major, orientation, time, gestureStart, isAod);
+                } else {
+                    mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+                }
             }
             for (Callback cb : mCallbacks) {
                 cb.onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt
deleted file mode 100644
index 8ae4775..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.graphics.Point
-import android.graphics.Rect
-import android.util.RotationUtils
-import android.view.MotionEvent
-import kotlin.math.cos
-import kotlin.math.pow
-import kotlin.math.sin
-
-private const val TAG = "UdfpsEllipseDetection"
-
-private const val NEEDED_POINTS = 2
-
-class UdfpsEllipseDetection(overlayParams: UdfpsOverlayParams) {
-    var sensorRect = Rect()
-    var points: Array<Point> = emptyArray()
-
-    init {
-        sensorRect = Rect(overlayParams.sensorBounds)
-
-        points = calculateSensorPoints(sensorRect)
-    }
-
-    fun updateOverlayParams(params: UdfpsOverlayParams) {
-        sensorRect = Rect(params.sensorBounds)
-
-        val rot = params.rotation
-        RotationUtils.rotateBounds(
-            sensorRect,
-            params.naturalDisplayWidth,
-            params.naturalDisplayHeight,
-            rot
-        )
-
-        points = calculateSensorPoints(sensorRect)
-    }
-
-    fun isGoodEllipseOverlap(event: MotionEvent): Boolean {
-        return points.count { checkPoint(event, it) } >= NEEDED_POINTS
-    }
-
-    private fun checkPoint(event: MotionEvent, point: Point): Boolean {
-        // Calculate if sensor point is within ellipse
-        // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
-        // yS))^2 / b^2) <= 1
-        val a: Float = cos(event.orientation) * (point.x - event.rawX)
-        val b: Float = sin(event.orientation) * (point.y - event.rawY)
-        val c: Float = sin(event.orientation) * (point.x - event.rawX)
-        val d: Float = cos(event.orientation) * (point.y - event.rawY)
-        val result =
-            (a + b).pow(2) / (event.touchMinor / 2).pow(2) +
-                (c - d).pow(2) / (event.touchMajor / 2).pow(2)
-
-        return result <= 1
-    }
-}
-
-fun calculateSensorPoints(sensorRect: Rect): Array<Point> {
-    val sensorX = sensorRect.centerX()
-    val sensorY = sensorRect.centerY()
-    val cornerOffset: Int = sensorRect.width() / 4
-    val sideOffset: Int = sensorRect.width() / 3
-
-    return arrayOf(
-        Point(sensorX - cornerOffset, sensorY - cornerOffset),
-        Point(sensorX, sensorY - sideOffset),
-        Point(sensorX + cornerOffset, sensorY - cornerOffset),
-        Point(sensorX - sideOffset, sensorY),
-        Point(sensorX, sensorY),
-        Point(sensorX + sideOffset, sensorY),
-        Point(sensorX - cornerOffset, sensorY + cornerOffset),
-        Point(sensorX, sensorY + sideOffset),
-        Point(sensorX + cornerOffset, sensorY + cornerOffset)
-    )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
index c23b0f0..7f3846c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
@@ -7,17 +7,23 @@
 /**
  * Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
  *
- * @property sensorBounds coordinates of the bounding box around the sensor, in natural orientation,
- *     in pixels, for the current resolution.
- * @property naturalDisplayWidth width of the physical display, in natural orientation, in pixels,
- *     for the current resolution.
- * @property naturalDisplayHeight height of the physical display, in natural orientation, in pixels,
- *     for the current resolution.
- * @property scaleFactor ratio of a dimension in the current resolution to the corresponding
- *     dimension in the native resolution.
- * @property rotation current rotation of the display.
+ * [sensorBounds] coordinates of the bounding box around the sensor in natural orientation, in
+ * pixels, for the current resolution.
+ *
+ * [overlayBounds] coordinates of the UI overlay in natural orientation, in pixels, for the current
+ * resolution.
+ *
+ * [naturalDisplayWidth] width of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [naturalDisplayHeight] height of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [scaleFactor] ratio of a dimension in the current resolution to the corresponding dimension in
+ * the native resolution.
+ *
+ * [rotation] current rotation of the display.
  */
-
 data class UdfpsOverlayParams(
     val sensorBounds: Rect = Rect(),
     val overlayBounds: Rect = Rect(),
@@ -26,17 +32,21 @@
     val scaleFactor: Float = 1f,
     @Rotation val rotation: Int = Surface.ROTATION_0
 ) {
+
+    /** Same as [sensorBounds], but in native resolution. */
+    val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) }
+
     /** See [android.view.DisplayInfo.logicalWidth] */
-    val logicalDisplayWidth
-        get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+    val logicalDisplayWidth =
+        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
             naturalDisplayHeight
         } else {
             naturalDisplayWidth
         }
 
     /** See [android.view.DisplayInfo.logicalHeight] */
-    val logicalDisplayHeight
-        get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+    val logicalDisplayHeight =
+        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
             naturalDisplayWidth
         } else {
             naturalDisplayHeight
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
new file mode 100644
index 0000000..001fed7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.dagger
+
+import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
+import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
+import com.android.systemui.biometrics.udfps.OverlapDetector
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import dagger.Module
+import dagger.Provides
+
+/** Dagger module for all things UDFPS. TODO(b/260558624): Move to BiometricsModule. */
+@Module
+interface UdfpsModule {
+    companion object {
+
+        @Provides
+        @SysUISingleton
+        fun providesOverlapDetector(featureFlags: FeatureFlags): OverlapDetector {
+            return if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
+                EllipseOverlapDetector()
+            } else {
+                BoundingBoxOverlapDetector()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
new file mode 100644
index 0000000..79a0acb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import com.android.systemui.dagger.SysUISingleton
+
+/** Returns whether the touch coordinates are within the sensor's bounding box. */
+@SysUISingleton
+class BoundingBoxOverlapDetector : OverlapDetector {
+    override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean =
+        touchData.isWithinSensor(nativeSensorBounds)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
new file mode 100644
index 0000000..8572242
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Point
+import android.graphics.Rect
+import com.android.systemui.dagger.SysUISingleton
+import kotlin.math.cos
+import kotlin.math.pow
+import kotlin.math.sin
+
+/**
+ * Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap
+ * with the sensor.
+ */
+@SysUISingleton
+class EllipseOverlapDetector(private val neededPoints: Int = 2) : OverlapDetector {
+
+    override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
+        val points = calculateSensorPoints(nativeSensorBounds)
+        return points.count { checkPoint(it, touchData) } >= neededPoints
+    }
+
+    private fun checkPoint(point: Point, touchData: NormalizedTouchData): Boolean {
+        // Calculate if sensor point is within ellipse
+        // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
+        // yS))^2 / b^2) <= 1
+        val a: Float = cos(touchData.orientation) * (point.x - touchData.x)
+        val b: Float = sin(touchData.orientation) * (point.y - touchData.y)
+        val c: Float = sin(touchData.orientation) * (point.x - touchData.x)
+        val d: Float = cos(touchData.orientation) * (point.y - touchData.y)
+        val result =
+            (a + b).pow(2) / (touchData.minor / 2).pow(2) +
+                (c - d).pow(2) / (touchData.major / 2).pow(2)
+
+        return result <= 1
+    }
+
+    private fun calculateSensorPoints(sensorBounds: Rect): List<Point> {
+        val sensorX = sensorBounds.centerX()
+        val sensorY = sensorBounds.centerY()
+        val cornerOffset: Int = sensorBounds.width() / 4
+        val sideOffset: Int = sensorBounds.width() / 3
+
+        return listOf(
+            Point(sensorX - cornerOffset, sensorY - cornerOffset),
+            Point(sensorX, sensorY - sideOffset),
+            Point(sensorX + cornerOffset, sensorY - cornerOffset),
+            Point(sensorX - sideOffset, sensorY),
+            Point(sensorX, sensorY),
+            Point(sensorX + sideOffset, sensorY),
+            Point(sensorX - cornerOffset, sensorY + cornerOffset),
+            Point(sensorX, sensorY + sideOffset),
+            Point(sensorX + cornerOffset, sensorY + cornerOffset)
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt
new file mode 100644
index 0000000..6e47dad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+
+/** Interaction event between a finger and the under-display fingerprint sensor (UDFPS). */
+enum class InteractionEvent {
+    /**
+     * A finger entered the sensor area. This can originate from either [MotionEvent.ACTION_DOWN] or
+     * [MotionEvent.ACTION_MOVE].
+     */
+    DOWN,
+
+    /**
+     * A finger left the sensor area. This can originate from either [MotionEvent.ACTION_UP] or
+     * [MotionEvent.ACTION_MOVE].
+     */
+    UP,
+
+    /**
+     * The touch reporting has stopped. This corresponds to [MotionEvent.ACTION_CANCEL]. This should
+     * not be confused with [UP]. If there was a finger on the sensor, it may or may not still be on
+     * the sensor.
+     */
+    CANCEL,
+
+    /**
+     * The interaction hasn't changed since the previous event. The can originate from any of
+     * [MotionEvent.ACTION_DOWN], [MotionEvent.ACTION_MOVE], or [MotionEvent.ACTION_UP] if one of
+     * these is true:
+     * - There was previously a finger on the sensor, and that finger is still on the sensor.
+     * - There was previously no finger on the sensor, and there still isn't.
+     */
+    UNCHANGED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
new file mode 100644
index 0000000..62bedc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import android.view.MotionEvent
+
+/** Touch data in natural orientation and native resolution. */
+data class NormalizedTouchData(
+
+    /**
+     * Value obtained from [MotionEvent.getPointerId], or [MotionEvent.INVALID_POINTER_ID] if the ID
+     * is not available.
+     */
+    val pointerId: Int,
+
+    /** [MotionEvent.getRawX] mapped to natural orientation and native resolution. */
+    val x: Float,
+
+    /** [MotionEvent.getRawY] mapped to natural orientation and native resolution. */
+    val y: Float,
+
+    /** [MotionEvent.getTouchMinor] mapped to natural orientation and native resolution. */
+    val minor: Float,
+
+    /** [MotionEvent.getTouchMajor] mapped to natural orientation and native resolution. */
+    val major: Float,
+
+    /** [MotionEvent.getOrientation] mapped to natural orientation. */
+    val orientation: Float,
+
+    /** [MotionEvent.getEventTime]. */
+    val time: Long,
+
+    /** [MotionEvent.getDownTime]. */
+    val gestureStart: Long,
+) {
+
+    /**
+     * [nativeSensorBounds] contains the location and dimensions of the sensor area in native
+     * resolution and natural orientation.
+     *
+     * Returns whether the coordinates of the given pointer are within the sensor's bounding box.
+     */
+    fun isWithinSensor(nativeSensorBounds: Rect): Boolean {
+        return nativeSensorBounds.left <= x &&
+            nativeSensorBounds.right >= x &&
+            nativeSensorBounds.top <= y &&
+            nativeSensorBounds.bottom >= y
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt
new file mode 100644
index 0000000..0fec8ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+
+/** Determines whether the touch has a sufficient overlap with the sensor. */
+interface OverlapDetector {
+    fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
new file mode 100644
index 0000000..338bf66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.PointF
+import android.util.RotationUtils
+import android.view.MotionEvent
+import android.view.MotionEvent.INVALID_POINTER_ID
+import android.view.Surface
+import com.android.systemui.biometrics.UdfpsOverlayParams
+import com.android.systemui.biometrics.udfps.TouchProcessorResult.Failure
+import com.android.systemui.biometrics.udfps.TouchProcessorResult.ProcessedTouch
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * TODO(b/259140693): Consider using an object pool of TouchProcessorResult to avoid allocations.
+ */
+@SysUISingleton
+class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: OverlapDetector) :
+    TouchProcessor {
+
+    override fun processTouch(
+        event: MotionEvent,
+        previousPointerOnSensorId: Int,
+        overlayParams: UdfpsOverlayParams,
+    ): TouchProcessorResult {
+
+        fun preprocess(): PreprocessedTouch {
+            // TODO(b/253085297): Add multitouch support. pointerIndex can be > 0 for ACTION_MOVE.
+            val pointerIndex = 0
+            val touchData = event.normalize(pointerIndex, overlayParams)
+            val isGoodOverlap =
+                overlapDetector.isGoodOverlap(touchData, overlayParams.nativeSensorBounds)
+            return PreprocessedTouch(touchData, previousPointerOnSensorId, isGoodOverlap)
+        }
+
+        return when (event.actionMasked) {
+            MotionEvent.ACTION_DOWN -> processActionDown(preprocess())
+            MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
+            MotionEvent.ACTION_UP -> processActionUp(preprocess())
+            MotionEvent.ACTION_CANCEL ->
+                processActionCancel(event.normalize(pointerIndex = 0, overlayParams))
+            else ->
+                Failure("Unsupported MotionEvent." + MotionEvent.actionToString(event.actionMasked))
+        }
+    }
+}
+
+private data class PreprocessedTouch(
+    val data: NormalizedTouchData,
+    val previousPointerOnSensorId: Int,
+    val isGoodOverlap: Boolean,
+)
+
+private fun processActionDown(touch: PreprocessedTouch): TouchProcessorResult {
+    return if (touch.isGoodOverlap) {
+        ProcessedTouch(InteractionEvent.DOWN, pointerOnSensorId = touch.data.pointerId, touch.data)
+    } else {
+        val event =
+            if (touch.data.pointerId == touch.previousPointerOnSensorId) {
+                InteractionEvent.UP
+            } else {
+                InteractionEvent.UNCHANGED
+            }
+        ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+    }
+}
+
+private fun processActionMove(touch: PreprocessedTouch): TouchProcessorResult {
+    val hadPointerOnSensor = touch.previousPointerOnSensorId != INVALID_POINTER_ID
+    val interactionEvent =
+        when {
+            touch.isGoodOverlap && !hadPointerOnSensor -> InteractionEvent.DOWN
+            !touch.isGoodOverlap && hadPointerOnSensor -> InteractionEvent.UP
+            else -> InteractionEvent.UNCHANGED
+        }
+    val pointerOnSensorId =
+        when (interactionEvent) {
+            InteractionEvent.UNCHANGED -> touch.previousPointerOnSensorId
+            InteractionEvent.DOWN -> touch.data.pointerId
+            else -> INVALID_POINTER_ID
+        }
+    return ProcessedTouch(interactionEvent, pointerOnSensorId, touch.data)
+}
+
+private fun processActionUp(touch: PreprocessedTouch): TouchProcessorResult {
+    return if (touch.isGoodOverlap) {
+        ProcessedTouch(InteractionEvent.UP, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+    } else {
+        val event =
+            if (touch.previousPointerOnSensorId != INVALID_POINTER_ID) {
+                InteractionEvent.UP
+            } else {
+                InteractionEvent.UNCHANGED
+            }
+        ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+    }
+}
+
+private fun processActionCancel(data: NormalizedTouchData): TouchProcessorResult {
+    return ProcessedTouch(InteractionEvent.CANCEL, pointerOnSensorId = INVALID_POINTER_ID, data)
+}
+
+/**
+ * Returns the touch information from the given [MotionEvent] with the relevant fields mapped to
+ * natural orientation and native resolution.
+ */
+private fun MotionEvent.normalize(
+    pointerIndex: Int,
+    overlayParams: UdfpsOverlayParams
+): NormalizedTouchData {
+    val naturalTouch: PointF = rotateToNaturalOrientation(pointerIndex, overlayParams)
+    val nativeX = naturalTouch.x / overlayParams.scaleFactor
+    val nativeY = naturalTouch.y / overlayParams.scaleFactor
+    val nativeMinor: Float = getTouchMinor(pointerIndex) / overlayParams.scaleFactor
+    val nativeMajor: Float = getTouchMajor(pointerIndex) / overlayParams.scaleFactor
+    return NormalizedTouchData(
+        pointerId = getPointerId(pointerIndex),
+        x = nativeX,
+        y = nativeY,
+        minor = nativeMinor,
+        major = nativeMajor,
+        // TODO(b/259311354): touch orientation should be reported relative to Surface.ROTATION_O.
+        orientation = getOrientation(pointerIndex),
+        time = eventTime,
+        gestureStart = downTime,
+    )
+}
+
+/**
+ * Returns the [MotionEvent.getRawX] and [MotionEvent.getRawY] of the given pointer as if the device
+ * is in the [Surface.ROTATION_0] orientation.
+ */
+private fun MotionEvent.rotateToNaturalOrientation(
+    pointerIndex: Int,
+    overlayParams: UdfpsOverlayParams
+): PointF {
+    val touchPoint = PointF(getRawX(pointerIndex), getRawY(pointerIndex))
+    val rot = overlayParams.rotation
+    if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+        RotationUtils.rotatePointF(
+            touchPoint,
+            RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
+            overlayParams.logicalDisplayWidth.toFloat(),
+            overlayParams.logicalDisplayHeight.toFloat()
+        )
+    }
+    return touchPoint
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt
new file mode 100644
index 0000000..ffcebf9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+import com.android.systemui.biometrics.UdfpsOverlayParams
+
+/**
+ * Determines whether a finger entered or left the area of the under-display fingerprint sensor
+ * (UDFPS). Maps the touch information from a [MotionEvent] to the orientation and scale independent
+ * [NormalizedTouchData].
+ */
+interface TouchProcessor {
+
+    /**
+     * [event] touch event to be processed.
+     *
+     * [previousPointerOnSensorId] pointerId for the finger that was on the sensor prior to this
+     * event. See [MotionEvent.getPointerId]. If there was no finger on the sensor, this should be
+     * set to [MotionEvent.INVALID_POINTER_ID].
+     *
+     * [overlayParams] contains the location and dimensions of the sensor area, as well as the scale
+     * factor and orientation of the overlay. See [UdfpsOverlayParams].
+     *
+     * Returns [TouchProcessorResult.ProcessedTouch] on success, and [TouchProcessorResult.Failure]
+     * on failure.
+     */
+    fun processTouch(
+        event: MotionEvent,
+        previousPointerOnSensorId: Int,
+        overlayParams: UdfpsOverlayParams,
+    ): TouchProcessorResult
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt
new file mode 100644
index 0000000..be75bb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+
+/** Contains all the possible returns types for [TouchProcessor.processTouch] */
+sealed class TouchProcessorResult {
+
+    /**
+     * [event] whether a finger entered or left the sensor area. See [InteractionEvent].
+     *
+     * [pointerOnSensorId] pointerId fof the finger that's currently on the sensor. See
+     * [MotionEvent.getPointerId]. If there is no finger on the sensor, the value is set to
+     * [MotionEvent.INVALID_POINTER_ID].
+     *
+     * [touchData] relevant data from the MotionEvent, mapped to natural orientation and native
+     * resolution. See [NormalizedTouchData].
+     */
+    data class ProcessedTouch(
+        val event: InteractionEvent,
+        val pointerOnSensorId: Int,
+        val touchData: NormalizedTouchData
+    ) : TouchProcessorResult()
+
+    /** [reason] the reason for the failure. */
+    data class Failure(val reason: String = "") : TouchProcessorResult()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt
new file mode 100644
index 0000000..3d10ab9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import kotlinx.coroutines.flow.StateFlow
+
+/** Repository for Device controls related settings. */
+interface ControlsSettingsRepository {
+    /** Whether device controls activity can be shown above lockscreen for this user. */
+    val canShowControlsInLockscreen: StateFlow<Boolean>
+
+    /** Whether trivial controls can be actioned from the lockscreen for this user. */
+    val allowActionOnTrivialControlsInLockscreen: StateFlow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt
new file mode 100644
index 0000000..9dc422a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.SettingObserver
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * This implementation uses an `@Application` [CoroutineScope] to provide hot flows for the values
+ * of the tracked settings.
+ */
+@SysUISingleton
+class ControlsSettingsRepositoryImpl
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val userRepository: UserRepository,
+    private val secureSettings: SecureSettings
+) : ControlsSettingsRepository {
+
+    override val canShowControlsInLockscreen =
+        makeFlowForSetting(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS)
+
+    override val allowActionOnTrivialControlsInLockscreen =
+        makeFlowForSetting(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    private fun makeFlowForSetting(setting: String): StateFlow<Boolean> {
+        return userRepository.selectedUserInfo
+            .distinctUntilChanged()
+            .flatMapLatest { userInfo ->
+                conflatedCallbackFlow {
+                        val observer =
+                            object : SettingObserver(secureSettings, null, setting, userInfo.id) {
+                                override fun handleValueChanged(
+                                    value: Int,
+                                    observedChange: Boolean
+                                ) {
+                                    trySend(value == 1)
+                                }
+                            }
+                        observer.isListening = true
+                        trySend(observer.value == 1)
+                        awaitClose { observer.isListening = false }
+                    }
+                    .flowOn(backgroundDispatcher)
+                    .distinctUntilChanged()
+            }
+            .stateIn(
+                scope,
+                started = SharingStarted.Eagerly,
+                // When the observer starts listening, the flow will emit the current value
+                // so the initialValue here is irrelevant.
+                initialValue = false,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 870649d..7b1c623 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -34,7 +34,6 @@
 import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID
 import com.android.systemui.Dumpable
 import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.management.ControlsListingController
@@ -62,11 +61,10 @@
     private val uiController: ControlsUiController,
     private val bindingController: ControlsBindingController,
     private val listingController: ControlsListingController,
-    private val broadcastDispatcher: BroadcastDispatcher,
     private val userFileManager: UserFileManager,
+    private val userTracker: UserTracker,
     optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
     dumpManager: DumpManager,
-    userTracker: UserTracker
 ) : Dumpable, ControlsController {
 
     companion object {
@@ -123,18 +121,15 @@
         userChanging = false
     }
 
-    private val userSwitchReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            if (intent.action == Intent.ACTION_USER_SWITCHED) {
-                userChanging = true
-                val newUser =
-                        UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
-                if (currentUser == newUser) {
-                    userChanging = false
-                    return
-                }
-                setValuesForUser(newUser)
+    private val userTrackerCallback = object : UserTracker.Callback {
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            userChanging = true
+            val newUserHandle = UserHandle.of(newUser)
+            if (currentUser == newUserHandle) {
+                userChanging = false
+                return
             }
+            setValuesForUser(newUserHandle)
         }
     }
 
@@ -236,12 +231,7 @@
         dumpManager.registerDumpable(javaClass.name, this)
         resetFavorites()
         userChanging = false
-        broadcastDispatcher.registerReceiver(
-                userSwitchReceiver,
-                IntentFilter(Intent.ACTION_USER_SWITCHED),
-                executor,
-                UserHandle.ALL
-        )
+        userTracker.addCallback(userTrackerCallback, executor)
         context.registerReceiver(
             restoreFinishedReceiver,
             IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
@@ -253,7 +243,7 @@
     }
 
     fun destroy() {
-        broadcastDispatcher.unregisterReceiver(userSwitchReceiver)
+        userTracker.removeCallback(userTrackerCallback)
         context.unregisterReceiver(restoreFinishedReceiver)
         listingController.removeCallback(listingCallback)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 9e4a364..77d0496e4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -16,13 +16,10 @@
 
 package com.android.systemui.controls.dagger
 
-import android.content.ContentResolver
 import android.content.Context
-import android.database.ContentObserver
-import android.os.UserHandle
-import android.provider.Settings
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.systemui.controls.ControlsSettingsRepository
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
 import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
@@ -31,12 +28,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
 import dagger.Lazy
+import kotlinx.coroutines.flow.StateFlow
 import java.util.Optional
 import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
 
 /**
  * Pseudo-component to inject into classes outside `com.android.systemui.controls`.
@@ -46,47 +41,26 @@
  */
 @SysUISingleton
 class ControlsComponent @Inject constructor(
-    @ControlsFeatureEnabled private val featureEnabled: Boolean,
-    private val context: Context,
-    private val lazyControlsController: Lazy<ControlsController>,
-    private val lazyControlsUiController: Lazy<ControlsUiController>,
-    private val lazyControlsListingController: Lazy<ControlsListingController>,
-    private val lockPatternUtils: LockPatternUtils,
-    private val keyguardStateController: KeyguardStateController,
-    private val userTracker: UserTracker,
-    private val secureSettings: SecureSettings,
-    private val optionalControlsTileResourceConfiguration:
-        Optional<ControlsTileResourceConfiguration>
+        @ControlsFeatureEnabled private val featureEnabled: Boolean,
+        private val context: Context,
+        private val lazyControlsController: Lazy<ControlsController>,
+        private val lazyControlsUiController: Lazy<ControlsUiController>,
+        private val lazyControlsListingController: Lazy<ControlsListingController>,
+        private val lockPatternUtils: LockPatternUtils,
+        private val keyguardStateController: KeyguardStateController,
+        private val userTracker: UserTracker,
+        controlsSettingsRepository: ControlsSettingsRepository,
+        optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration>
 ) {
-    private val contentResolver: ContentResolver
-        get() = context.contentResolver
 
-    private val _canShowWhileLockedSetting = MutableStateFlow(false)
-    val canShowWhileLockedSetting = _canShowWhileLockedSetting.asStateFlow()
+    val canShowWhileLockedSetting: StateFlow<Boolean> =
+            controlsSettingsRepository.canShowControlsInLockscreen
 
     private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration =
         optionalControlsTileResourceConfiguration.orElse(
             ControlsTileResourceConfigurationImpl()
         )
 
-    val showWhileLockedObserver = object : ContentObserver(null) {
-        override fun onChange(selfChange: Boolean) {
-            updateShowWhileLocked()
-        }
-    }
-
-    init {
-        if (featureEnabled) {
-            secureSettings.registerContentObserverForUser(
-                Settings.Secure.getUriFor(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
-                false, /* notifyForDescendants */
-                showWhileLockedObserver,
-                UserHandle.USER_ALL
-            )
-            updateShowWhileLocked()
-        }
-    }
-
     fun getControlsController(): Optional<ControlsController> {
         return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty()
     }
@@ -127,11 +101,6 @@
         return Visibility.AVAILABLE
     }
 
-    private fun updateShowWhileLocked() {
-        _canShowWhileLockedSetting.value = secureSettings.getIntForUser(
-            Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
-    }
-
     enum class Visibility {
         AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 6f58abd..9ae605e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -20,6 +20,8 @@
 import android.content.pm.PackageManager
 import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.ControlsMetricsLoggerImpl
+import com.android.systemui.controls.ControlsSettingsRepository
+import com.android.systemui.controls.ControlsSettingsRepositoryImpl
 import com.android.systemui.controls.controller.ControlsBindingController
 import com.android.systemui.controls.controller.ControlsBindingControllerImpl
 import com.android.systemui.controls.controller.ControlsController
@@ -83,6 +85,11 @@
     abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
 
     @Binds
+    abstract fun provideSettingsManager(
+            manager: ControlsSettingsRepositoryImpl
+    ): ControlsSettingsRepository
+
+    @Binds
     abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 1f7021e..041ed1d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -25,9 +25,6 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
 import android.os.UserHandle
 import android.os.VibrationEffect
 import android.provider.Settings.Secure
@@ -41,6 +38,7 @@
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.ControlsSettingsRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -69,17 +67,17 @@
     private val vibrator: VibratorHelper,
     private val secureSettings: SecureSettings,
     private val userContextProvider: UserContextProvider,
-    @Main mainHandler: Handler
+    private val controlsSettingsRepository: ControlsSettingsRepository,
 ) : ControlActionCoordinator {
     private var dialog: Dialog? = null
     private var pendingAction: Action? = null
     private var actionsInProgress = mutableSetOf<String>()
     private val isLocked: Boolean
         get() = !keyguardStateController.isUnlocked()
-    private var mAllowTrivialControls: Boolean = secureSettings.getIntForUser(
-            Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
-    private var mShowDeviceControlsInLockscreen: Boolean = secureSettings.getIntForUser(
-            Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
+    private val allowTrivialControls: Boolean
+        get() = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value
+    private val showDeviceControlsInLockscreen: Boolean
+        get() = controlsSettingsRepository.canShowControlsInLockscreen.value
     override lateinit var activityContext: Context
 
     companion object {
@@ -87,38 +85,6 @@
         private const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2
     }
 
-    init {
-        val lockScreenShowControlsUri =
-            secureSettings.getUriFor(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
-        val showControlsUri =
-                secureSettings.getUriFor(Secure.LOCKSCREEN_SHOW_CONTROLS)
-        val controlsContentObserver = object : ContentObserver(mainHandler) {
-            override fun onChange(selfChange: Boolean, uri: Uri?) {
-                super.onChange(selfChange, uri)
-                when (uri) {
-                    lockScreenShowControlsUri -> {
-                        mAllowTrivialControls = secureSettings.getIntForUser(
-                                Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
-                                0, UserHandle.USER_CURRENT) != 0
-                    }
-                    showControlsUri -> {
-                        mShowDeviceControlsInLockscreen = secureSettings
-                                .getIntForUser(Secure.LOCKSCREEN_SHOW_CONTROLS,
-                                        0, UserHandle.USER_CURRENT) != 0
-                    }
-                }
-            }
-        }
-        secureSettings.registerContentObserverForUser(
-            lockScreenShowControlsUri,
-            false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
-        )
-        secureSettings.registerContentObserverForUser(
-            showControlsUri,
-            false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
-        )
-    }
-
     override fun closeDialogs() {
         val isActivityFinishing =
             (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
@@ -233,7 +199,7 @@
     @AnyThread
     @VisibleForTesting
     fun bouncerOrRun(action: Action) {
-        val authRequired = action.authIsRequired || !mAllowTrivialControls
+        val authRequired = action.authIsRequired || !allowTrivialControls
 
         if (keyguardStateController.isShowing() && authRequired) {
             if (isLocked) {
@@ -291,7 +257,7 @@
                 PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
         val attempts = prefs.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)
         if (attempts >= MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG ||
-                (mShowDeviceControlsInLockscreen && mAllowTrivialControls)) {
+                (showDeviceControlsInLockscreen && allowTrivialControls)) {
             return
         }
         val builder = AlertDialog
@@ -313,7 +279,7 @@
                     true
                 }
 
-        if (mShowDeviceControlsInLockscreen) {
+        if (showDeviceControlsInLockscreen) {
             dialog = builder
                     .setTitle(R.string.controls_settings_trivial_controls_dialog_title)
                     .setMessage(R.string.controls_settings_trivial_controls_dialog_message)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index d60a222..3d8e4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -19,7 +19,6 @@
 import android.content.BroadcastReceiver;
 
 import com.android.systemui.GuestResetOrExitSessionReceiver;
-import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -106,15 +105,6 @@
      */
     @Binds
     @IntoMap
-    @ClassKey(GuestResumeSessionReceiver.class)
-    public abstract BroadcastReceiver bindGuestResumeSessionReceiver(
-            GuestResumeSessionReceiver broadcastReceiver);
-
-    /**
-     *
-     */
-    @Binds
-    @IntoMap
     @ClassKey(GuestResetOrExitSessionReceiver.class)
     public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver(
             GuestResetOrExitSessionReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index bcf5e7a..3a59f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -33,6 +33,7 @@
 import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
 import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
 import com.android.systemui.biometrics.dagger.BiometricsModule;
+import com.android.systemui.biometrics.dagger.UdfpsModule;
 import com.android.systemui.classifier.FalsingModule;
 import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
 import com.android.systemui.controls.dagger.ControlsModule;
@@ -154,6 +155,7 @@
             TelephonyRepositoryModule.class,
             TemporaryDisplayModule.class,
             TunerModule.class,
+            UdfpsModule.class,
             UserModule.class,
             UtilModule.class,
             NoteTaskModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 0b69b80..5daf1ce 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -29,12 +29,13 @@
 import android.content.IntentFilter;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.text.format.Formatter;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.view.Display;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEvent;
@@ -100,6 +101,7 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final AuthController mAuthController;
     private final KeyguardStateController mKeyguardStateController;
+    private final UserTracker mUserTracker;
     private final UiEventLogger mUiEventLogger;
 
     private long mNotificationPulseTime;
@@ -110,6 +112,14 @@
     private boolean mWantTouchScreenSensors;
     private boolean mWantSensors;
 
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    mDozeSensors.onUserSwitched();
+                }
+            };
+
     @VisibleForTesting
     public enum DozingUpdateUiEvent implements UiEventLogger.UiEventEnum {
         @UiEvent(doc = "Dozing updated due to notification.")
@@ -210,6 +220,7 @@
         mAuthController = authController;
         mUiEventLogger = uiEventLogger;
         mKeyguardStateController = keyguardStateController;
+        mUserTracker = userTracker;
     }
 
     @Override
@@ -234,7 +245,7 @@
             return;
         }
         mNotificationPulseTime = SystemClock.elapsedRealtime();
-        if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
+        if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
             runIfNotNull(onPulseSuppressedListener);
             mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
             return;
@@ -490,12 +501,14 @@
         mBroadcastReceiver.register(mBroadcastDispatcher);
         mDockManager.addListener(mDockEventListener);
         mDozeHost.addCallback(mHostCallback);
+        mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
     }
 
     private void unregisterCallbacks() {
         mBroadcastReceiver.unregister(mBroadcastDispatcher);
         mDozeHost.removeCallback(mHostCallback);
         mDockManager.removeListener(mDockEventListener);
+        mUserTracker.removeCallback(mUserChangedCallback);
     }
 
     private void stopListeningToAllTriggers() {
@@ -620,9 +633,6 @@
                 requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
                         null /* onPulseSuppressedListener */);
             }
-            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
-                mDozeSensors.onUserSwitched();
-            }
         }
 
         public void register(BroadcastDispatcher broadcastDispatcher) {
@@ -630,7 +640,6 @@
                 return;
             }
             IntentFilter filter = new IntentFilter(PULSE_ACTION);
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
             broadcastDispatcher.registerReceiver(this, filter);
             mRegistered = true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 48159ae..46ce7a9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -192,9 +192,7 @@
                         break;
                 }
 
-                // Add margin if specified by the complication. Otherwise add default margin
-                // between complications.
-                if (mLayoutParams.isMarginSpecified() || !isRoot) {
+                if (!isRoot) {
                     final int margin = mLayoutParams.getMargin(mDefaultMargin);
                     switch(direction) {
                         case ComplicationLayoutParams.DIRECTION_DOWN:
@@ -213,6 +211,19 @@
                 }
             });
 
+            if (mLayoutParams.constraintSpecified()) {
+                switch (direction) {
+                    case ComplicationLayoutParams.DIRECTION_START:
+                    case ComplicationLayoutParams.DIRECTION_END:
+                        params.matchConstraintMaxWidth = mLayoutParams.getConstraint();
+                        break;
+                    case ComplicationLayoutParams.DIRECTION_UP:
+                    case ComplicationLayoutParams.DIRECTION_DOWN:
+                        params.matchConstraintMaxHeight = mLayoutParams.getConstraint();
+                        break;
+                }
+            }
+
             mView.setLayoutParams(params);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index 4fae68d..1755cb92 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -52,6 +52,7 @@
     private static final int LAST_POSITION = POSITION_END;
 
     private static final int MARGIN_UNSPECIFIED = 0xFFFFFFFF;
+    private static final int CONSTRAINT_UNSPECIFIED = 0xFFFFFFFF;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "DIRECTION_" }, value = {
@@ -81,6 +82,8 @@
 
     private final int mMargin;
 
+    private final int mConstraint;
+
     private final boolean mSnapToGuide;
 
     // Do not allow specifying opposite positions
@@ -110,7 +113,8 @@
      */
     public ComplicationLayoutParams(int width, int height, @Position int position,
             @Direction int direction, int weight) {
-        this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, false);
+        this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, CONSTRAINT_UNSPECIFIED,
+                false);
     }
 
     /**
@@ -127,7 +131,27 @@
      */
     public ComplicationLayoutParams(int width, int height, @Position int position,
             @Direction int direction, int weight, int margin) {
-        this(width, height, position, direction, weight, margin, false);
+        this(width, height, position, direction, weight, margin, CONSTRAINT_UNSPECIFIED, false);
+    }
+
+    /**
+     * Constructs a {@link ComplicationLayoutParams}.
+     * @param width The width {@link android.view.View.MeasureSpec} for the view.
+     * @param height The height {@link android.view.View.MeasureSpec} for the view.
+     * @param position The place within the parent container where the view should be positioned.
+     * @param direction The direction the view should be laid out from either the parent container
+     *                  or preceding view.
+     * @param weight The weight that should be considered for this view when compared to other
+     *               views. This has an impact on the placement of the view but not the rendering of
+     *               the view.
+     * @param margin The margin to apply between complications.
+     * @param constraint The max width or height the complication is allowed to spread, depending on
+     *                   its direction. For horizontal directions, this would be applied on width,
+     *                   and for vertical directions, height.
+     */
+    public ComplicationLayoutParams(int width, int height, @Position int position,
+            @Direction int direction, int weight, int margin, int constraint) {
+        this(width, height, position, direction, weight, margin, constraint, false);
     }
 
     /**
@@ -148,7 +172,8 @@
      */
     public ComplicationLayoutParams(int width, int height, @Position int position,
             @Direction int direction, int weight, boolean snapToGuide) {
-        this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, snapToGuide);
+        this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, CONSTRAINT_UNSPECIFIED,
+                snapToGuide);
     }
 
     /**
@@ -162,6 +187,9 @@
      *               views. This has an impact on the placement of the view but not the rendering of
      *               the view.
      * @param margin The margin to apply between complications.
+     * @param constraint The max width or height the complication is allowed to spread, depending on
+     *                   its direction. For horizontal directions, this would be applied on width,
+     *                   and for vertical directions, height.
      * @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction
      *                    will be automatically set to align with a predetermined guide for that
      *                    side. For example, if the complication is aligned to the top end and
@@ -169,7 +197,7 @@
      *                    from the end of the parent to the guide.
      */
     public ComplicationLayoutParams(int width, int height, @Position int position,
-            @Direction int direction, int weight, int margin, boolean snapToGuide) {
+            @Direction int direction, int weight, int margin, int constraint, boolean snapToGuide) {
         super(width, height);
 
         if (!validatePosition(position)) {
@@ -187,6 +215,8 @@
 
         mMargin = margin;
 
+        mConstraint = constraint;
+
         mSnapToGuide = snapToGuide;
     }
 
@@ -199,6 +229,7 @@
         mDirection = source.mDirection;
         mWeight = source.mWeight;
         mMargin = source.mMargin;
+        mConstraint = source.mConstraint;
         mSnapToGuide = source.mSnapToGuide;
     }
 
@@ -261,13 +292,6 @@
     }
 
     /**
-     * Returns whether margin has been specified by the complication.
-     */
-    public boolean isMarginSpecified() {
-        return mMargin != MARGIN_UNSPECIFIED;
-    }
-
-    /**
      * Returns the margin to apply between complications, or the given default if no margin is
      * specified.
      */
@@ -276,6 +300,21 @@
     }
 
     /**
+     * Returns whether the horizontal or vertical constraint has been specified.
+     */
+    public boolean constraintSpecified() {
+        return mConstraint != CONSTRAINT_UNSPECIFIED;
+    }
+
+    /**
+     * Returns the horizontal or vertical constraint of the complication, depending its direction.
+     * For horizontal directions, this is the max width, and for vertical directions, max height.
+     */
+    public int getConstraint() {
+        return mConstraint;
+    }
+
+    /**
      * Returns whether the complication's dimension perpendicular to direction should be
      * automatically set.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index c01cf43..ee00512 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -32,6 +32,7 @@
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.controls.ControlsServiceInfo;
 import com.android.systemui.controls.dagger.ControlsComponent;
@@ -151,7 +152,7 @@
         @Inject
         DreamHomeControlsChipViewHolder(
                 DreamHomeControlsChipViewController dreamHomeControlsChipViewController,
-                @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
+                @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) View view,
                 @Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
         ) {
             mView = view;
@@ -174,7 +175,7 @@
     /**
      * Controls behavior of the dream complication.
      */
-    static class DreamHomeControlsChipViewController extends ViewController<ImageView> {
+    static class DreamHomeControlsChipViewController extends ViewController<View> {
         private static final String TAG = "DreamHomeControlsCtrl";
         private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -203,7 +204,7 @@
 
         @Inject
         DreamHomeControlsChipViewController(
-                @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
+                @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) View view,
                 ActivityStarter activityStarter,
                 Context context,
                 ControlsComponent controlsComponent,
@@ -218,9 +219,10 @@
 
         @Override
         protected void onViewAttached() {
-            mView.setImageResource(mControlsComponent.getTileImageId());
-            mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
-            mView.setOnClickListener(this::onClickHomeControls);
+            final ImageView chip = mView.findViewById(R.id.home_controls_chip);
+            chip.setImageResource(mControlsComponent.getTileImageId());
+            chip.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
+            chip.setOnClickListener(this::onClickHomeControls);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
index 7d9f105..5290e44 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
@@ -45,11 +45,12 @@
     @Provides
     @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
     static View provideComplicationView(LayoutInflater layoutInflater) {
-        final TextClock view = Preconditions.checkNotNull((TextClock)
+        final View view = Preconditions.checkNotNull(
                         layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
                                 null, false),
                 "R.layout.dream_overlay_complication_clock_time did not properly inflated");
-        view.setFontVariationSettings(TAG_WEIGHT + WEIGHT);
+        ((TextClock) view.findViewById(R.id.time_view)).setFontVariationSettings(
+                TAG_WEIGHT + WEIGHT);
         return view;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
index cf05d2d..a7aa97f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
@@ -19,7 +19,7 @@
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import android.view.LayoutInflater;
-import android.widget.ImageView;
+import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.dreams.complication.DreamHomeControlsComplication;
@@ -74,8 +74,8 @@
         @Provides
         @DreamHomeControlsComplicationScope
         @Named(DREAM_HOME_CONTROLS_CHIP_VIEW)
-        static ImageView provideHomeControlsChipView(LayoutInflater layoutInflater) {
-            return (ImageView) layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
+        static View provideHomeControlsChipView(LayoutInflater layoutInflater) {
+            return layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
                     null, false);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index a514c47..9b954f5f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -47,10 +47,10 @@
     String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
 
     int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
-    int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 0;
+    int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 2;
     int DREAM_MEDIA_COMPLICATION_WEIGHT = 0;
-    int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 2;
-    int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 1;
+    int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 4;
+    int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 3;
 
     /**
      * Provides layout parameters for the clock time complication.
@@ -72,17 +72,14 @@
      */
     @Provides
     @Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS)
-    static ComplicationLayoutParams provideHomeControlsChipLayoutParams(@Main Resources res) {
+    static ComplicationLayoutParams provideHomeControlsChipLayoutParams() {
         return new ComplicationLayoutParams(
-                res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
-                res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
                 ComplicationLayoutParams.POSITION_BOTTOM
                         | ComplicationLayoutParams.POSITION_START,
-                ComplicationLayoutParams.DIRECTION_UP,
-                DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT,
-                // Add margin to the bottom of home controls to horizontally align with smartspace.
-                res.getDimensionPixelSize(
-                        R.dimen.dream_overlay_complication_home_controls_padding));
+                ComplicationLayoutParams.DIRECTION_END,
+                DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
     }
 
     /**
@@ -106,12 +103,14 @@
     @Provides
     @Named(DREAM_SMARTSPACE_LAYOUT_PARAMS)
     static ComplicationLayoutParams provideSmartspaceLayoutParams(@Main Resources res) {
-        return new ComplicationLayoutParams(0,
+        return new ComplicationLayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 ComplicationLayoutParams.POSITION_BOTTOM
                         | ComplicationLayoutParams.POSITION_START,
                 ComplicationLayoutParams.DIRECTION_END,
                 DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
-                res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding));
+                res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
+                res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2d558ad..8019b56 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -164,6 +164,13 @@
     // TODO(b/256513609): Tracking Bug
     @JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
 
+    /**
+     * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
+     * new KeyguardTransitionRepository.
+     */
+    @JvmField
+    val LIGHT_REVEAL_MIGRATION = unreleasedFlag(218, "light_reveal_migration", teamfood = true)
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -205,15 +212,6 @@
     @JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag(508, "qs_secondary_data_sub_info")
 
     // 600- status bar
-    // TODO(b/254512623): Tracking Bug
-    @Deprecated("Replaced by mobile and wifi specific flags.")
-    val NEW_STATUS_BAR_PIPELINE_BACKEND =
-        unreleasedFlag(604, "new_status_bar_pipeline_backend", teamfood = false)
-
-    // TODO(b/254512660): Tracking Bug
-    @Deprecated("Replaced by mobile and wifi specific flags.")
-    val NEW_STATUS_BAR_PIPELINE_FRONTEND =
-        unreleasedFlag(605, "new_status_bar_pipeline_frontend", teamfood = false)
 
     // TODO(b/256614753): Tracking Bug
     val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
@@ -231,6 +229,10 @@
     // TODO(b/256623670): Tracking Bug
     @JvmField val BATTERY_SHIELD_ICON = unreleasedFlag(610, "battery_shield_icon")
 
+    // TODO(b/260881289): Tracking Bug
+    val NEW_STATUS_BAR_ICONS_DEBUG_COLORING =
+        unreleasedFlag(611, "new_status_bar_icons_debug_coloring")
+
     // 700 - dialer/calls
     // TODO(b/254512734): Tracking Bug
     val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
@@ -375,7 +377,7 @@
     // TODO(b/255854141): Tracking Bug
     @JvmField
     val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
-        unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = false)
+        unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
 
     // 1300 - screenshots
     // TODO(b/254512719): Tracking Bug
@@ -401,17 +403,15 @@
 
     // 1700 - clipboard
     @JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor")
-    @JvmField
-    val CLIPBOARD_REMOTE_BEHAVIOR =
-        unreleasedFlag(1701, "clipboard_remote_behavior", teamfood = true)
+    @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
 
     // 1800 - shade container
     @JvmField
     val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
         unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
 
-    // 1900 - note task
-    @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+    // 1900
+    @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
 
     // 2000 - device controls
     @Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
@@ -428,6 +428,12 @@
     // 2300 - stylus
     @JvmField val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used")
 
+    // 2400 - performance tools and debugging info
+    // TODO(b/238923086): Tracking Bug
+    @JvmField
+    val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
+        unreleasedFlag(2400, "warn_on_blocking_binder_transactions")
+
     // TODO(b259590361): Tracking bug
     val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 49527d3..62fe80a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -21,50 +21,52 @@
 import com.android.systemui.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.statusbar.policy.FlashlightController
+import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import javax.inject.Inject
 
 @SysUISingleton
-class FlashlightQuickAffordanceConfig @Inject constructor(
-        @Application private val context: Context,
-        private val flashlightController: FlashlightController,
+class FlashlightQuickAffordanceConfig
+@Inject
+constructor(
+    @Application private val context: Context,
+    private val flashlightController: FlashlightController,
 ) : KeyguardQuickAffordanceConfig {
 
     private sealed class FlashlightState {
 
         abstract fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState
 
-        object On: FlashlightState() {
+        object On : FlashlightState() {
             override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     Icon.Resource(
-                        R.drawable.ic_flashlight_on,
+                        R.drawable.qs_flashlight_icon_on,
                         ContentDescription.Resource(R.string.quick_settings_flashlight_label)
                     ),
                     ActivationState.Active
                 )
         }
 
-        object OffAvailable: FlashlightState() {
+        object OffAvailable : FlashlightState() {
             override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                     Icon.Resource(
-                        R.drawable.ic_flashlight_off,
+                        R.drawable.qs_flashlight_icon_off,
                         ContentDescription.Resource(R.string.quick_settings_flashlight_label)
                     ),
                     ActivationState.Inactive
                 )
         }
 
-        object Unavailable: FlashlightState() {
+        object Unavailable : FlashlightState() {
             override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
                 KeyguardQuickAffordanceConfig.LockScreenState.Hidden
         }
@@ -77,57 +79,57 @@
         get() = context.getString(R.string.quick_settings_flashlight_label)
 
     override val pickerIconResourceId: Int
-        get() = if (flashlightController.isEnabled) {
-            R.drawable.ic_flashlight_on
-        } else {
-            R.drawable.ic_flashlight_off
-        }
+        get() = R.drawable.ic_flashlight_off
 
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
-            conflatedCallbackFlow {
-        val flashlightCallback = object : FlashlightController.FlashlightListener {
-            override fun onFlashlightChanged(enabled: Boolean) {
-                trySendWithFailureLogging(
-                    if (enabled) {
-                        FlashlightState.On.toLockScreenState()
-                    } else {
-                        FlashlightState.OffAvailable.toLockScreenState()
-                    },
-                    TAG
-                )
-            }
+        conflatedCallbackFlow {
+            val flashlightCallback =
+                object : FlashlightController.FlashlightListener {
+                    override fun onFlashlightChanged(enabled: Boolean) {
+                        trySendWithFailureLogging(
+                            if (enabled) {
+                                FlashlightState.On.toLockScreenState()
+                            } else {
+                                FlashlightState.OffAvailable.toLockScreenState()
+                            },
+                            TAG
+                        )
+                    }
 
-            override fun onFlashlightError() {
-                trySendWithFailureLogging(FlashlightState.OffAvailable.toLockScreenState(), TAG)
-            }
+                    override fun onFlashlightError() {
+                        trySendWithFailureLogging(
+                            FlashlightState.OffAvailable.toLockScreenState(),
+                            TAG
+                        )
+                    }
 
-            override fun onFlashlightAvailabilityChanged(available: Boolean) {
-                trySendWithFailureLogging(
-                    if (!available) {
-                        FlashlightState.Unavailable.toLockScreenState()
-                    } else {
-                        if (flashlightController.isEnabled) {
-                            FlashlightState.On.toLockScreenState()
-                        } else {
-                            FlashlightState.OffAvailable.toLockScreenState()
-                        }
-                    },
-                    TAG
-                )
-            }
+                    override fun onFlashlightAvailabilityChanged(available: Boolean) {
+                        trySendWithFailureLogging(
+                            if (!available) {
+                                FlashlightState.Unavailable.toLockScreenState()
+                            } else {
+                                if (flashlightController.isEnabled) {
+                                    FlashlightState.On.toLockScreenState()
+                                } else {
+                                    FlashlightState.OffAvailable.toLockScreenState()
+                                }
+                            },
+                            TAG
+                        )
+                    }
+                }
+
+            flashlightController.addCallback(flashlightCallback)
+
+            awaitClose { flashlightController.removeCallback(flashlightCallback) }
         }
 
-        flashlightController.addCallback(flashlightCallback)
-
-        awaitClose {
-            flashlightController.removeCallback(flashlightCallback)
-        }
-    }
-
-    override fun onTriggered(expandable: Expandable?):
-            KeyguardQuickAffordanceConfig.OnTriggeredResult {
-        flashlightController
-                .setFlashlight(flashlightController.isAvailable && !flashlightController.isEnabled)
+    override fun onTriggered(
+        expandable: Expandable?
+    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+        flashlightController.setFlashlight(
+            flashlightController.isAvailable && !flashlightController.isEnabled
+        )
         return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
     }
 
@@ -141,4 +143,4 @@
     companion object {
         private const val TAG = "FlashlightQuickAffordanceConfig"
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 3013227c..072cfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -17,27 +17,35 @@
 
 package com.android.systemui.keyguard.data.quickaffordance
 
+import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ElementsIntoSet
 
 @Module
-object KeyguardDataQuickAffordanceModule {
-    @Provides
-    @ElementsIntoSet
-    fun quickAffordanceConfigs(
-        flashlight: FlashlightQuickAffordanceConfig,
-        home: HomeControlsKeyguardQuickAffordanceConfig,
-        quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
-        qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
-        camera: CameraQuickAffordanceConfig,
-    ): Set<KeyguardQuickAffordanceConfig> {
-        return setOf(
-            camera,
-            flashlight,
-            home,
-            quickAccessWallet,
-            qrCodeScanner,
-        )
+interface KeyguardDataQuickAffordanceModule {
+    @Binds
+    fun providerClientFactory(
+        impl: KeyguardQuickAffordanceProviderClientFactoryImpl,
+    ): KeyguardQuickAffordanceProviderClientFactory
+
+    companion object {
+        @Provides
+        @ElementsIntoSet
+        fun quickAffordanceConfigs(
+            flashlight: FlashlightQuickAffordanceConfig,
+            home: HomeControlsKeyguardQuickAffordanceConfig,
+            quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
+            qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
+            camera: CameraQuickAffordanceConfig,
+        ): Set<KeyguardQuickAffordanceConfig> {
+            return setOf(
+                camera,
+                flashlight,
+                home,
+                quickAccessWallet,
+                qrCodeScanner,
+            )
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
index 766096f..72747f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
@@ -67,7 +67,7 @@
     @Application private val scope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val secureSettings: SecureSettings,
-    private val selectionsManager: KeyguardQuickAffordanceSelectionManager,
+    private val selectionsManager: KeyguardQuickAffordanceLocalUserSelectionManager,
 ) {
     companion object {
         private val BINDINGS =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
new file mode 100644
index 0000000..0066785
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.content.IntentFilter
+import android.content.SharedPreferences
+import com.android.systemui.R
+import com.android.systemui.backup.BackupHelper
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Manages and provides access to the current "selections" of keyguard quick affordances, answering
+ * the question "which affordances should the keyguard show?" for the user associated with the
+ * System UI process.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardQuickAffordanceLocalUserSelectionManager
+@Inject
+constructor(
+    @Application context: Context,
+    private val userFileManager: UserFileManager,
+    private val userTracker: UserTracker,
+    broadcastDispatcher: BroadcastDispatcher,
+) : KeyguardQuickAffordanceSelectionManager {
+
+    private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
+
+    private val userId: Flow<Int> = conflatedCallbackFlow {
+        val callback =
+            object : UserTracker.Callback {
+                override fun onUserChanged(newUser: Int, userContext: Context) {
+                    trySendWithFailureLogging(newUser, TAG)
+                }
+            }
+
+        userTracker.addCallback(callback) { it.run() }
+        trySendWithFailureLogging(userTracker.userId, TAG)
+
+        awaitClose { userTracker.removeCallback(callback) }
+    }
+
+    private val defaults: Map<String, List<String>> by lazy {
+        context.resources
+            .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
+            .associate { item ->
+                val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
+                check(splitUp.size == 2)
+                val slotId = splitUp[0]
+                val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
+                slotId to affordanceIds
+            }
+    }
+
+    /**
+     * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
+     * initial value.
+     */
+    private val backupRestorationEvents: Flow<Unit> =
+        broadcastDispatcher.broadcastFlow(
+            filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
+            flags = Context.RECEIVER_NOT_EXPORTED,
+            permission = BackupHelper.PERMISSION_SELF,
+        )
+
+    override val selections: Flow<Map<String, List<String>>> =
+        combine(
+                userId,
+                backupRestorationEvents.onStart {
+                    // We emit an initial event to make sure that the combine emits at least once,
+                    // even if we never get a Backup & Restore restoration event (which is the most
+                    // common case anyway as restoration really only happens on initial device
+                    // setup).
+                    emit(Unit)
+                }
+            ) { _, _ -> }
+            .flatMapLatest {
+                conflatedCallbackFlow {
+                    // We want to instantiate a new SharedPreferences instance each time either the
+                    // user ID changes or we have a backup & restore restoration event. The reason
+                    // is that our sharedPrefs instance needs to be replaced with a new one as it
+                    // depends on the user ID and when the B&R job completes, the backing file is
+                    // replaced but the existing instance still has a stale in-memory cache.
+                    sharedPrefs = instantiateSharedPrefs()
+
+                    val listener =
+                        SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
+                            trySend(getSelections())
+                        }
+
+                    sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
+                    send(getSelections())
+
+                    awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
+                }
+            }
+
+    override fun getSelections(): Map<String, List<String>> {
+        val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
+        val result =
+            slotKeys
+                .associate { key ->
+                    val slotId = key.substring(KEY_PREFIX_SLOT.length)
+                    val value = sharedPrefs.getString(key, null)
+                    val affordanceIds =
+                        if (!value.isNullOrEmpty()) {
+                            value.split(AFFORDANCE_DELIMITER)
+                        } else {
+                            emptyList()
+                        }
+                    slotId to affordanceIds
+                }
+                .toMutableMap()
+
+        // If the result map is missing keys, it means that the system has never set anything for
+        // those slots. This is where we need examine our defaults and see if there should be a
+        // default value for the affordances in the slot IDs that are missing from the result.
+        //
+        // Once the user makes any selection for a slot, even when they select "None", this class
+        // will persist a key for that slot ID. In the case of "None", it will have a value of the
+        // empty string. This is why this system works.
+        defaults.forEach { (slotId, affordanceIds) ->
+            if (!result.containsKey(slotId)) {
+                result[slotId] = affordanceIds
+            }
+        }
+
+        return result
+    }
+
+    override fun setSelections(
+        slotId: String,
+        affordanceIds: List<String>,
+    ) {
+        val key = "$KEY_PREFIX_SLOT$slotId"
+        val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
+        sharedPrefs.edit().putString(key, value).apply()
+    }
+
+    private fun instantiateSharedPrefs(): SharedPreferences {
+        return userFileManager.getSharedPreferences(
+            FILE_NAME,
+            Context.MODE_PRIVATE,
+            userTracker.userId,
+        )
+    }
+
+    companion object {
+        private const val TAG = "KeyguardQuickAffordancePrimaryUserSelectionManager"
+        const val FILE_NAME = "quick_affordance_selections"
+        private const val KEY_PREFIX_SLOT = "slot_"
+        private const val SLOT_AFFORDANCES_DELIMITER = ":"
+        private const val AFFORDANCE_DELIMITER = ","
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
new file mode 100644
index 0000000..727a813
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+
+interface KeyguardQuickAffordanceProviderClientFactory {
+    fun create(): KeyguardQuickAffordanceProviderClient
+}
+
+class KeyguardQuickAffordanceProviderClientFactoryImpl
+@Inject
+constructor(
+    private val userTracker: UserTracker,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : KeyguardQuickAffordanceProviderClientFactory {
+    override fun create(): KeyguardQuickAffordanceProviderClient {
+        return KeyguardQuickAffordanceProviderClientImpl(
+            context = userTracker.userContext,
+            backgroundDispatcher = backgroundDispatcher,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
new file mode 100644
index 0000000..8ffef25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/**
+ * Manages and provides access to the current "selections" of keyguard quick affordances, answering
+ * the question "which affordances should the keyguard show?" for users associated with other System
+ * UI processes.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardQuickAffordanceRemoteUserSelectionManager
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val userTracker: UserTracker,
+    private val clientFactory: KeyguardQuickAffordanceProviderClientFactory,
+    private val userHandle: UserHandle,
+) : KeyguardQuickAffordanceSelectionManager {
+
+    private val userId: Flow<Int> = conflatedCallbackFlow {
+        val callback =
+            object : UserTracker.Callback {
+                override fun onUserChanged(newUser: Int, userContext: Context) {
+                    trySendWithFailureLogging(newUser, TAG)
+                }
+            }
+
+        userTracker.addCallback(callback) { it.run() }
+        trySendWithFailureLogging(userTracker.userId, TAG)
+
+        awaitClose { userTracker.removeCallback(callback) }
+    }
+
+    private val clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
+        userId
+            .distinctUntilChanged()
+            .map { selectedUserId ->
+                if (userHandle.isSystem && userHandle.identifier != selectedUserId) {
+                    clientFactory.create()
+                } else {
+                    null
+                }
+            }
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.Eagerly,
+                initialValue = null,
+            )
+
+    private val _selections: StateFlow<Map<String, List<String>>> =
+        clientOrNull
+            .flatMapLatest { client ->
+                client?.observeSelections()?.map { selections ->
+                    buildMap<String, List<String>> {
+                        selections.forEach { selection ->
+                            val slotId = selection.slotId
+                            val affordanceIds = (get(slotId) ?: emptyList()).toMutableList()
+                            affordanceIds.add(selection.affordanceId)
+                            put(slotId, affordanceIds)
+                        }
+                    }
+                }
+                    ?: emptyFlow()
+            }
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.Eagerly,
+                initialValue = emptyMap(),
+            )
+
+    override val selections: Flow<Map<String, List<String>>> = _selections
+
+    override fun getSelections(): Map<String, List<String>> {
+        return _selections.value
+    }
+
+    override fun setSelections(slotId: String, affordanceIds: List<String>) {
+        clientOrNull.value?.let { client ->
+            scope.launch {
+                client.deleteAllSelections(slotId = slotId)
+                affordanceIds.forEach { affordanceId ->
+                    client.insertSelection(slotId = slotId, affordanceId = affordanceId)
+                }
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "KeyguardQuickAffordanceMultiUserSelectionManager"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
index 4f37e5f..21fffed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
@@ -17,153 +17,22 @@
 
 package com.android.systemui.keyguard.data.quickaffordance
 
-import android.content.Context
-import android.content.IntentFilter
-import android.content.SharedPreferences
-import com.android.systemui.R
-import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.settings.UserFileManager
-import com.android.systemui.settings.UserTracker
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.onStart
 
 /**
- * Manages and provides access to the current "selections" of keyguard quick affordances, answering
- * the question "which affordances should the keyguard show?".
+ * Defines interface for classes that manage and provide access to the current "selections" of
+ * keyguard quick affordances, answering the question "which affordances should the keyguard show?".
  */
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-class KeyguardQuickAffordanceSelectionManager
-@Inject
-constructor(
-    @Application context: Context,
-    private val userFileManager: UserFileManager,
-    private val userTracker: UserTracker,
-    broadcastDispatcher: BroadcastDispatcher,
-) {
-
-    private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
-
-    private val userId: Flow<Int> = conflatedCallbackFlow {
-        val callback =
-            object : UserTracker.Callback {
-                override fun onUserChanged(newUser: Int, userContext: Context) {
-                    trySendWithFailureLogging(newUser, TAG)
-                }
-            }
-
-        userTracker.addCallback(callback) { it.run() }
-        trySendWithFailureLogging(userTracker.userId, TAG)
-
-        awaitClose { userTracker.removeCallback(callback) }
-    }
-    private val defaults: Map<String, List<String>> by lazy {
-        context.resources
-            .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
-            .associate { item ->
-                val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
-                check(splitUp.size == 2)
-                val slotId = splitUp[0]
-                val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
-                slotId to affordanceIds
-            }
-    }
-
-    /**
-     * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
-     * initial value.
-     */
-    private val backupRestorationEvents: Flow<Unit> =
-        broadcastDispatcher.broadcastFlow(
-            filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
-            flags = Context.RECEIVER_NOT_EXPORTED,
-            permission = BackupHelper.PERMISSION_SELF,
-        )
+interface KeyguardQuickAffordanceSelectionManager {
 
     /** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */
-    val selections: Flow<Map<String, List<String>>> =
-        combine(
-                userId,
-                backupRestorationEvents.onStart {
-                    // We emit an initial event to make sure that the combine emits at least once,
-                    // even
-                    // if we never get a Backup & Restore restoration event (which is the most
-                    // common
-                    // case anyway as restoration really only happens on initial device setup).
-                    emit(Unit)
-                }
-            ) { _, _ ->
-            }
-            .flatMapLatest {
-                conflatedCallbackFlow {
-                    // We want to instantiate a new SharedPreferences instance each time either the
-                    // user
-                    // ID changes or we have a backup & restore restoration event. The reason is
-                    // that
-                    // our sharedPrefs instance needs to be replaced with a new one as it depends on
-                    // the
-                    // user ID and when the B&R job completes, the backing file is replaced but the
-                    // existing instance still has a stale in-memory cache.
-                    sharedPrefs = instantiateSharedPrefs()
-
-                    val listener =
-                        SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
-                            trySend(getSelections())
-                        }
-
-                    sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
-                    send(getSelections())
-
-                    awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
-                }
-            }
+    val selections: Flow<Map<String, List<String>>>
 
     /**
      * Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in
      * descending priority order.
      */
-    fun getSelections(): Map<String, List<String>> {
-        val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
-        val result =
-            slotKeys
-                .associate { key ->
-                    val slotId = key.substring(KEY_PREFIX_SLOT.length)
-                    val value = sharedPrefs.getString(key, null)
-                    val affordanceIds =
-                        if (!value.isNullOrEmpty()) {
-                            value.split(AFFORDANCE_DELIMITER)
-                        } else {
-                            emptyList()
-                        }
-                    slotId to affordanceIds
-                }
-                .toMutableMap()
-
-        // If the result map is missing keys, it means that the system has never set anything for
-        // those slots. This is where we need examine our defaults and see if there should be a
-        // default value for the affordances in the slot IDs that are missing from the result.
-        //
-        // Once the user makes any selection for a slot, even when they select "None", this class
-        // will persist a key for that slot ID. In the case of "None", it will have a value of the
-        // empty string. This is why this system works.
-        defaults.forEach { (slotId, affordanceIds) ->
-            if (!result.containsKey(slotId)) {
-                result[slotId] = affordanceIds
-            }
-        }
-
-        return result
-    }
+    fun getSelections(): Map<String, List<String>>
 
     /**
      * Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
@@ -172,25 +41,9 @@
     fun setSelections(
         slotId: String,
         affordanceIds: List<String>,
-    ) {
-        val key = "$KEY_PREFIX_SLOT$slotId"
-        val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
-        sharedPrefs.edit().putString(key, value).apply()
-    }
-
-    private fun instantiateSharedPrefs(): SharedPreferences {
-        return userFileManager.getSharedPreferences(
-            FILE_NAME,
-            Context.MODE_PRIVATE,
-            userTracker.userId,
-        )
-    }
+    )
 
     companion object {
-        private const val TAG = "KeyguardQuickAffordanceSelectionManager"
         const val FILE_NAME = "quick_affordance_selections"
-        private const val KEY_PREFIX_SLOT = "slot_"
-        private const val SLOT_AFFORDANCES_DELIMITER = ":"
-        private const val AFFORDANCE_DELIMITER = ","
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index d95a1a7..e3f5e90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -18,45 +18,93 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.content.Context
+import android.os.UserHandle
 import com.android.systemui.Dumpable
 import com.android.systemui.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.settings.UserTracker
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** Abstracts access to application state related to keyguard quick affordances. */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceRepository
 @Inject
 constructor(
     @Application private val appContext: Context,
     @Application private val scope: CoroutineScope,
-    private val selectionManager: KeyguardQuickAffordanceSelectionManager,
+    private val localUserSelectionManager: KeyguardQuickAffordanceLocalUserSelectionManager,
+    private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager,
+    private val userTracker: UserTracker,
     legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer,
     private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
     dumpManager: DumpManager,
+    userHandle: UserHandle,
 ) {
+    private val userId: Flow<Int> =
+        ConflatedCallbackFlow.conflatedCallbackFlow {
+            val callback =
+                object : UserTracker.Callback {
+                    override fun onUserChanged(newUser: Int, userContext: Context) {
+                        trySendWithFailureLogging(newUser, TAG)
+                    }
+                }
+
+            userTracker.addCallback(callback) { it.run() }
+            trySendWithFailureLogging(userTracker.userId, TAG)
+
+            awaitClose { userTracker.removeCallback(callback) }
+        }
+
+    private val selectionManager: StateFlow<KeyguardQuickAffordanceSelectionManager> =
+        userId
+            .distinctUntilChanged()
+            .map { selectedUserId ->
+                if (userHandle.identifier == selectedUserId) {
+                    localUserSelectionManager
+                } else {
+                    remoteUserSelectionManager
+                }
+            }
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.Eagerly,
+                initialValue = localUserSelectionManager,
+            )
+
     /**
      * List of [KeyguardQuickAffordanceConfig] instances of the affordances at the slot with the
      * given ID. The configs are sorted in descending priority order.
      */
     val selections: StateFlow<Map<String, List<KeyguardQuickAffordanceConfig>>> =
-        selectionManager.selections
-            .map { selectionsBySlotId ->
-                selectionsBySlotId.mapValues { (_, selections) ->
-                    configs.filter { selections.contains(it.key) }
+        selectionManager
+            .flatMapLatest { selectionManager ->
+                selectionManager.selections.map { selectionsBySlotId ->
+                    selectionsBySlotId.mapValues { (_, selections) ->
+                        configs.filter { selections.contains(it.key) }
+                    }
                 }
             }
             .stateIn(
@@ -99,7 +147,7 @@
      * slot with the given ID. The configs are sorted in descending priority order.
      */
     fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
-        val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList())
+        val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
         return configs.filter { selections.contains(it.key) }
     }
 
@@ -108,7 +156,7 @@
      * are sorted in descending priority order.
      */
     fun getSelections(): Map<String, List<String>> {
-        return selectionManager.getSelections()
+        return selectionManager.value.getSelections()
     }
 
     /**
@@ -119,7 +167,7 @@
         slotId: String,
         affordanceIds: List<String>,
     ) {
-        selectionManager.setSelections(
+        selectionManager.value.setSelections(
             slotId = slotId,
             affordanceIds = affordanceIds,
         )
@@ -188,6 +236,7 @@
     }
 
     companion object {
+        private const val TAG = "KeyguardQuickAffordanceRepository"
         private const val SLOT_CONFIG_DELIMITER = ":"
     }
 }
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 796f2b4..148792b 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
@@ -16,8 +16,11 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.biometrics.AuthController
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.Position
@@ -27,8 +30,8 @@
 import com.android.systemui.doze.DozeTransitionCallback
 import com.android.systemui.doze.DozeTransitionListener
 import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -88,8 +91,8 @@
      * enter to conserve battery when the device is locked and inactive.
      *
      * Note that it is possible for the system to be transitioning into doze while this flow still
-     * returns `false`. In order to account for that, observers should also use the [dozeAmount]
-     * flow to check if it's greater than `0`
+     * returns `false`. In order to account for that, observers should also use the
+     * [linearDozeAmount] flow to check if it's greater than `0`
      */
     val isDozing: Flow<Boolean>
 
@@ -111,7 +114,7 @@
      * happens during an animation/transition into doze mode. An observer would be wise to account
      * for both flows if needed.
      */
-    val dozeAmount: Flow<Float>
+    val linearDozeAmount: Flow<Float>
 
     /** Doze state information, as it transitions */
     val dozeTransitionModel: Flow<DozeTransitionModel>
@@ -120,11 +123,20 @@
     val statusBarState: Flow<StatusBarState>
 
     /** Observable for device wake/sleep state */
-    val wakefulnessState: Flow<WakefulnessModel>
+    val wakefulness: Flow<WakefulnessModel>
 
     /** Observable for biometric unlock modes */
     val biometricUnlockState: Flow<BiometricUnlockModel>
 
+    /** Approximate location on the screen of the fingerprint sensor. */
+    val fingerprintSensorLocation: Flow<Point?>
+
+    /** Approximate location on the screen of the face unlock sensor/front facing camera. */
+    val faceSensorLocation: Flow<Point?>
+
+    /** Source of the most recent biometric unlock, such as fingerprint or face. */
+    val biometricUnlockSource: Flow<BiometricUnlockSource?>
+
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
      *
@@ -163,6 +175,7 @@
     private val keyguardStateController: KeyguardStateController,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val dozeTransitionListener: DozeTransitionListener,
+    private val authController: AuthController,
 ) : KeyguardRepository {
     private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
     override val animateBottomAreaDozingTransitions =
@@ -281,11 +294,11 @@
             }
             .distinctUntilChanged()
 
-    override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
+    override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
         val callback =
             object : StatusBarStateController.StateListener {
                 override fun onDozeAmountChanged(linear: Float, eased: Float) {
-                    trySendWithFailureLogging(eased, TAG, "updated dozeAmount")
+                    trySendWithFailureLogging(linear, TAG, "updated dozeAmount")
                 }
             }
 
@@ -348,58 +361,139 @@
         awaitClose { statusBarStateController.removeCallback(callback) }
     }
 
-    override val wakefulnessState: Flow<WakefulnessModel> = conflatedCallbackFlow {
-        val callback =
-            object : WakefulnessLifecycle.Observer {
-                override fun onStartedWakingUp() {
-                    trySendWithFailureLogging(
-                        WakefulnessModel.STARTING_TO_WAKE,
-                        TAG,
-                        "Wakefulness: starting to wake"
-                    )
-                }
-                override fun onFinishedWakingUp() {
-                    trySendWithFailureLogging(WakefulnessModel.AWAKE, TAG, "Wakefulness: awake")
-                }
-                override fun onStartedGoingToSleep() {
-                    trySendWithFailureLogging(
-                        WakefulnessModel.STARTING_TO_SLEEP,
-                        TAG,
-                        "Wakefulness: starting to sleep"
-                    )
-                }
-                override fun onFinishedGoingToSleep() {
-                    trySendWithFailureLogging(WakefulnessModel.ASLEEP, TAG, "Wakefulness: asleep")
-                }
-            }
-        wakefulnessLifecycle.addObserver(callback)
-        trySendWithFailureLogging(
-            wakefulnessIntToObject(wakefulnessLifecycle.getWakefulness()),
-            TAG,
-            "initial wakefulness state"
-        )
-
-        awaitClose { wakefulnessLifecycle.removeObserver(callback) }
-    }
-
     override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+        fun dispatchUpdate() {
+            trySendWithFailureLogging(
+                biometricModeIntToObject(biometricUnlockController.mode),
+                TAG,
+                "biometric mode"
+            )
+        }
+
         val callback =
             object : BiometricUnlockController.BiometricModeListener {
                 override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
-                    trySendWithFailureLogging(biometricModeIntToObject(mode), TAG, "biometric mode")
+                    dispatchUpdate()
+                }
+
+                override fun onResetMode() {
+                    dispatchUpdate()
                 }
             }
 
         biometricUnlockController.addBiometricModeListener(callback)
-        trySendWithFailureLogging(
-            biometricModeIntToObject(biometricUnlockController.getMode()),
-            TAG,
-            "initial biometric mode"
-        )
+        dispatchUpdate()
 
         awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
     }
 
+    override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow {
+        val observer =
+            object : WakefulnessLifecycle.Observer {
+                override fun onStartedWakingUp() {
+                    dispatchNewState()
+                }
+
+                override fun onFinishedWakingUp() {
+                    dispatchNewState()
+                }
+
+                override fun onPostFinishedWakingUp() {
+                    dispatchNewState()
+                }
+
+                override fun onStartedGoingToSleep() {
+                    dispatchNewState()
+                }
+
+                override fun onFinishedGoingToSleep() {
+                    dispatchNewState()
+                }
+
+                private fun dispatchNewState() {
+                    trySendWithFailureLogging(
+                        WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
+                        TAG,
+                        "updated wakefulness state"
+                    )
+                }
+            }
+
+        wakefulnessLifecycle.addObserver(observer)
+        trySendWithFailureLogging(
+            WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
+            TAG,
+            "initial wakefulness state"
+        )
+
+        awaitClose { wakefulnessLifecycle.removeObserver(observer) }
+    }
+
+    override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+        fun sendFpLocation() {
+            trySendWithFailureLogging(
+                authController.fingerprintSensorLocation,
+                TAG,
+                "AuthController.Callback#onFingerprintLocationChanged"
+            )
+        }
+
+        val callback =
+            object : AuthController.Callback {
+                override fun onFingerprintLocationChanged() {
+                    sendFpLocation()
+                }
+            }
+
+        authController.addCallback(callback)
+        sendFpLocation()
+
+        awaitClose { authController.removeCallback(callback) }
+    }
+
+    override val faceSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+        fun sendSensorLocation() {
+            trySendWithFailureLogging(
+                authController.faceSensorLocation,
+                TAG,
+                "AuthController.Callback#onFingerprintLocationChanged"
+            )
+        }
+
+        val callback =
+            object : AuthController.Callback {
+                override fun onFaceSensorLocationChanged() {
+                    sendSensorLocation()
+                }
+            }
+
+        authController.addCallback(callback)
+        sendSensorLocation()
+
+        awaitClose { authController.removeCallback(callback) }
+    }
+
+    override val biometricUnlockSource: Flow<BiometricUnlockSource?> = conflatedCallbackFlow {
+        val callback =
+            object : KeyguardUpdateMonitorCallback() {
+                override fun onBiometricAuthenticated(
+                    userId: Int,
+                    biometricSourceType: BiometricSourceType?,
+                    isStrongBiometric: Boolean
+                ) {
+                    trySendWithFailureLogging(
+                        BiometricUnlockSource.fromBiometricSourceType(biometricSourceType),
+                        TAG,
+                        "onBiometricAuthenticated"
+                    )
+                }
+            }
+
+        keyguardUpdateMonitor.registerCallback(callback)
+        trySendWithFailureLogging(null, TAG, "initial value")
+        awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+    }
+
     override fun setAnimateDozingTransitions(animate: Boolean) {
         _animateBottomAreaDozingTransitions.value = animate
     }
@@ -423,16 +517,6 @@
         }
     }
 
-    private fun wakefulnessIntToObject(@Wakefulness value: Int): WakefulnessModel {
-        return when (value) {
-            0 -> WakefulnessModel.ASLEEP
-            1 -> WakefulnessModel.STARTING_TO_WAKE
-            2 -> WakefulnessModel.AWAKE
-            3 -> WakefulnessModel.STARTING_TO_SLEEP
-            else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
-        }
-    }
-
     private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
         return when (value) {
             0 -> BiometricUnlockModel.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 0c72520..26f853f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -27,4 +27,7 @@
     fun keyguardTransitionRepository(
         impl: KeyguardTransitionRepositoryImpl
     ): KeyguardTransitionRepository
+
+    @Binds
+    fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
new file mode 100644
index 0000000..a17481a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import android.graphics.Point
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.PowerButtonReveal
+import javax.inject.Inject
+import kotlin.math.max
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+val DEFAULT_REVEAL_EFFECT = LiftReveal
+
+/**
+ * Encapsulates state relevant to the light reveal scrim, the view used to reveal/hide screen
+ * contents during transitions between AOD and lockscreen/unlocked.
+ */
+interface LightRevealScrimRepository {
+
+    /**
+     * The reveal effect that should be used for the next lock/unlock. We switch between either the
+     * biometric unlock effect (if wake and unlocking) or the non-biometric effect, and position it
+     * at the current screen position of the appropriate sensor.
+     */
+    val revealEffect: Flow<LightRevealEffect>
+}
+
+@SysUISingleton
+class LightRevealScrimRepositoryImpl
+@Inject
+constructor(
+    keyguardRepository: KeyguardRepository,
+    val context: Context,
+) : LightRevealScrimRepository {
+
+    /** The reveal effect used if the device was locked/unlocked via the power button. */
+    private val powerButtonReveal =
+        PowerButtonReveal(
+            context.resources
+                .getDimensionPixelSize(R.dimen.physical_power_button_center_screen_location_y)
+                .toFloat()
+        )
+
+    /**
+     * Reveal effect to use for a fingerprint unlock. This is reconstructed if the fingerprint
+     * sensor location on the screen (in pixels) changes due to configuration changes.
+     */
+    private val fingerprintRevealEffect: Flow<LightRevealEffect?> =
+        keyguardRepository.fingerprintSensorLocation.map {
+            it?.let { constructCircleRevealFromPoint(it) }
+        }
+
+    /**
+     * Reveal effect to use for a face unlock. This is reconstructed if the face sensor/front camera
+     * location on the screen (in pixels) changes due to configuration changes.
+     */
+    private val faceRevealEffect: Flow<LightRevealEffect?> =
+        keyguardRepository.faceSensorLocation.map { it?.let { constructCircleRevealFromPoint(it) } }
+
+    /**
+     * The reveal effect we'll use for the next biometric unlock animation. We switch between the
+     * fingerprint/face unlock effect flows depending on the biometric unlock source.
+     */
+    private val biometricRevealEffect: Flow<LightRevealEffect?> =
+        keyguardRepository.biometricUnlockSource.flatMapLatest { source ->
+            when (source) {
+                BiometricUnlockSource.FINGERPRINT_SENSOR -> fingerprintRevealEffect
+                BiometricUnlockSource.FACE_SENSOR -> faceRevealEffect
+                else -> flowOf(null)
+            }
+        }
+
+    /** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */
+    private val nonBiometricRevealEffect: Flow<LightRevealEffect?> =
+        keyguardRepository.wakefulness.map { wakefulnessModel ->
+            val wakingUpFromPowerButton =
+                wakefulnessModel.isWakingUpOrAwake &&
+                    wakefulnessModel.lastWakeReason == WakeSleepReason.POWER_BUTTON
+            val sleepingFromPowerButton =
+                !wakefulnessModel.isWakingUpOrAwake &&
+                    wakefulnessModel.lastSleepReason == WakeSleepReason.POWER_BUTTON
+
+            if (wakingUpFromPowerButton || sleepingFromPowerButton) {
+                powerButtonReveal
+            } else {
+                LiftReveal
+            }
+        }
+
+    override val revealEffect =
+        combine(
+                keyguardRepository.biometricUnlockState,
+                biometricRevealEffect,
+                nonBiometricRevealEffect
+            ) { biometricUnlockState, biometricReveal, nonBiometricReveal ->
+
+                // Use the biometric reveal for any flavor of wake and unlocking.
+                when (biometricUnlockState) {
+                    BiometricUnlockModel.WAKE_AND_UNLOCK,
+                    BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+                    BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
+                    else -> nonBiometricReveal
+                }
+                    ?: DEFAULT_REVEAL_EFFECT
+            }
+            .distinctUntilChanged()
+
+    private fun constructCircleRevealFromPoint(point: Point): LightRevealEffect {
+        return with(point) {
+            CircleReveal(
+                x,
+                y,
+                startRadius = 0,
+                endRadius =
+                    max(max(x, context.display.width - x), max(y, context.display.height - y)),
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 2dbacd5..9b19353 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
index 9e2b724..e34981e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
@@ -42,7 +42,7 @@
 
     override fun start() {
         scope.launch {
-            keyguardInteractor.wakefulnessState
+            keyguardInteractor.wakefulnessModel
                 .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
                 .collect { pair ->
                     val (wakefulnessState, keyguardState) = pair
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
index 0e2a54c..483041a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
@@ -23,11 +23,10 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -42,13 +41,13 @@
 
     override fun start() {
         scope.launch {
-            keyguardInteractor.wakefulnessState
+            keyguardInteractor.wakefulnessModel
                 .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
                 .collect { pair ->
                     val (wakefulnessState, keyguardState) = pair
                     if (
                         keyguardState == KeyguardState.GONE &&
-                            wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP
+                            wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
                     ) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
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 7cfd117..6912e1d 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
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.graphics.Point
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
@@ -41,7 +42,7 @@
      * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
      * all.
      */
-    val dozeAmount: Flow<Float> = repository.dozeAmount
+    val dozeAmount: Flow<Float> = repository.linearDozeAmount
     /** Whether the system is in doze mode. */
     val isDozing: Flow<Boolean> = repository.isDozing
     /** Doze transition information. */
@@ -58,7 +59,7 @@
     /** Whether the bouncer is showing or not. */
     val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
     /** The device wake/sleep state */
-    val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
+    val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
     /** Observable for the [StatusBarState] */
     val statusBarState: Flow<StatusBarState> = repository.statusBarState
     /**
@@ -67,10 +68,15 @@
      */
     val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
 
+    /** The approximate location on the screen of the fingerprint sensor, if one is available. */
+    val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
+
+    /** The approximate location on the screen of the face unlock sensor, if one is available. */
+    val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+
     fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> {
         return dozeTransitionModel.filter { it.to == state }
     }
-
     fun isKeyguardShowing(): Boolean {
         return repository.isKeyguardShowing()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ee7154f..748c6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -190,8 +190,6 @@
 
     /** Returns affordance IDs indexed by slot ID, for all known slots. */
     suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
-        check(isUsingRepository)
-
         val slots = repository.get().getSlotPickerRepresentations()
         val selections = repository.get().getSelections()
         val affordanceById =
@@ -312,8 +310,6 @@
 
     suspend fun getAffordancePickerRepresentations():
         List<KeyguardQuickAffordancePickerRepresentation> {
-        check(isUsingRepository)
-
         return repository.get().getAffordancePickerRepresentations()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 58a8093..e30e7f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -37,7 +37,7 @@
 
     fun start() {
         scope.launch {
-            keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
+            keyguardInteractor.wakefulnessModel.collect { logger.v("WakefulnessModel", it) }
         }
 
         scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
new file mode 100644
index 0000000..6e25200
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LightRevealScrimInteractor
+@Inject
+constructor(
+    transitionRepository: KeyguardTransitionRepository,
+    transitionInteractor: KeyguardTransitionInteractor,
+    lightRevealScrimRepository: LightRevealScrimRepository,
+) {
+
+    /**
+     * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
+     * and use that for the starting transition.
+     *
+     * We can't simply use the nextRevealEffect since the effect may change midway through a
+     * transition, but we don't want to change effects part way through. For example, if we're using
+     * a CircleReveal to animate a biometric unlock, but the biometric unlock mode changes to NONE
+     * from WAKE_AND_UNLOCK before the unlock animation ends, we don't want to end up switching to a
+     * LiftReveal.
+     */
+    val lightRevealEffect: Flow<LightRevealEffect> =
+        transitionInteractor.startedKeyguardTransitionStep.sample(
+            lightRevealScrimRepository.revealEffect
+        )
+
+    /**
+     * The reveal amount to use for the light reveal scrim, which is derived from the keyguard
+     * transition steps.
+     */
+    val revealAmount: Flow<Float> =
+        transitionRepository.transitions
+            // Only listen to transitions that change the reveal amount.
+            .filter { willTransitionAffectRevealAmount(it) }
+            // Use the transition amount as the reveal amount, inverting it if we're transitioning
+            // to a non-revealed (hidden) state.
+            .map { step -> if (willBeRevealedInState(step.to)) step.value else 1f - step.value }
+
+    companion object {
+
+        /**
+         * Whether the transition requires a change in the reveal amount of the light reveal scrim.
+         * If not, we don't care about the transition and don't need to listen to it.
+         */
+        fun willTransitionAffectRevealAmount(transition: TransitionStep): Boolean {
+            return willBeRevealedInState(transition.from) != willBeRevealedInState(transition.to)
+        }
+
+        /**
+         * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+         * state after the transition is complete. If false, scrim will be fully hidden.
+         */
+        fun willBeRevealedInState(state: KeyguardState): Boolean {
+            return when (state) {
+                KeyguardState.OFF -> false
+                KeyguardState.DOZING -> false
+                KeyguardState.AOD -> false
+                KeyguardState.DREAMING -> true
+                KeyguardState.BOUNCER -> true
+                KeyguardState.LOCKSCREEN -> true
+                KeyguardState.GONE -> true
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3bb8241..3218f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.sample
 import java.util.UUID
@@ -58,22 +58,26 @@
             keyguardInteractor.isBouncerShowing
                 .sample(
                     combine(
-                        keyguardInteractor.wakefulnessState,
+                        keyguardInteractor.wakefulnessModel,
                         keyguardTransitionInteractor.startedKeyguardTransitionStep,
-                    ) { a, b ->
-                        Pair(a, b)
-                    },
-                    { a, bc -> Triple(a, bc.first, bc.second) }
-                )
-                .collect { triple ->
-                    val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+                    ) { wakefulnessModel, transitionStep ->
+                        Pair(wakefulnessModel, transitionStep)
+                    }
+                ) { bouncerShowing, wakefulnessAndTransition ->
+                    Triple(
+                        bouncerShowing,
+                        wakefulnessAndTransition.first,
+                        wakefulnessAndTransition.second
+                    )
+                }
+                .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) ->
                     if (
                         !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
                     ) {
                         val to =
                             if (
-                                wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
-                                    wakefulnessState == WakefulnessModel.ASLEEP
+                                wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
+                                    wakefulnessState.state == WakefulnessState.ASLEEP
                             ) {
                                 KeyguardState.AOD
                             } else {
@@ -100,14 +104,17 @@
                     combine(
                         keyguardTransitionInteractor.finishedKeyguardState,
                         keyguardInteractor.statusBarState,
-                    ) { a, b ->
-                        Pair(a, b)
-                    },
-                    { a, bc -> Triple(a, bc.first, bc.second) }
-                )
-                .collect { triple ->
-                    val (shadeModel, keyguardState, statusBarState) = triple
-
+                    ) { finishedKeyguardState, statusBarState ->
+                        Pair(finishedKeyguardState, statusBarState)
+                    }
+                ) { shadeModel, keyguardStateAndStatusBarState ->
+                    Triple(
+                        shadeModel,
+                        keyguardStateAndStatusBarState.first,
+                        keyguardStateAndStatusBarState.second
+                    )
+                }
+                .collect { (shadeModel, keyguardState, statusBarState) ->
                     val id = transitionId
                     if (id != null) {
                         // An existing `id` means a transition is started, and calls to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
new file mode 100644
index 0000000..b403416
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.hardware.biometrics.BiometricSourceType
+
+/** Biometric unlock sensor sources, which we use to play sensor-specific animations. */
+enum class BiometricUnlockSource {
+    /** The unlock was initiated by a fingerprint sensor authentication. */
+    FINGERPRINT_SENSOR,
+
+    /** The unlock was initiated by the front-facing camera or a nearby sensor. */
+    FACE_SENSOR;
+
+    companion object {
+        fun fromBiometricSourceType(type: BiometricSourceType?): BiometricUnlockSource? {
+            return when (type) {
+                BiometricSourceType.FINGERPRINT -> FINGERPRINT_SENSOR
+                BiometricSourceType.FACE -> FACE_SENSOR
+                BiometricSourceType.IRIS -> FACE_SENSOR
+                else -> null
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
new file mode 100644
index 0000000..b32597d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.os.PowerManager
+
+/** The reason we're waking up or going to sleep, such as pressing the power button. */
+enum class WakeSleepReason {
+    /** The physical power button was pressed to wake up or sleep the device. */
+    POWER_BUTTON,
+
+    /** Something else happened to wake up or sleep the device. */
+    OTHER;
+
+    companion object {
+        fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
+            return when (reason) {
+                PowerManager.WAKE_REASON_POWER_BUTTON -> POWER_BUTTON
+                else -> OTHER
+            }
+        }
+
+        fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
+            return when (reason) {
+                PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+                else -> OTHER
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index 92040f4..03dee00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -15,24 +15,34 @@
  */
 package com.android.systemui.keyguard.shared.model
 
-/** Model device wakefulness states. */
-enum class WakefulnessModel {
-    /** The device is asleep and not interactive. */
-    ASLEEP,
-    /** Received a signal that the device is beginning to wake up. */
-    STARTING_TO_WAKE,
-    /** Device is now fully awake and interactive. */
-    AWAKE,
-    /** Signal that the device is now going to sleep. */
-    STARTING_TO_SLEEP;
+import com.android.systemui.keyguard.WakefulnessLifecycle
 
+/** Model device wakefulness states. */
+data class WakefulnessModel(
+    val state: WakefulnessState,
+    val isWakingUpOrAwake: Boolean,
+    val lastWakeReason: WakeSleepReason,
+    val lastSleepReason: WakeSleepReason,
+) {
     companion object {
         fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
-            return model == ASLEEP || model == STARTING_TO_SLEEP
+            return model.state == WakefulnessState.ASLEEP ||
+                model.state == WakefulnessState.STARTING_TO_SLEEP
         }
 
         fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
-            return model == AWAKE || model == STARTING_TO_WAKE
+            return model.state == WakefulnessState.AWAKE ||
+                model.state == WakefulnessState.STARTING_TO_WAKE
+        }
+
+        fun fromWakefulnessLifecycle(wakefulnessLifecycle: WakefulnessLifecycle): WakefulnessModel {
+            return WakefulnessModel(
+                WakefulnessState.fromWakefulnessLifecycleInt(wakefulnessLifecycle.wakefulness),
+                wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_WAKING ||
+                    wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE,
+                WakeSleepReason.fromPowerManagerWakeReason(wakefulnessLifecycle.lastWakeReason),
+                WakeSleepReason.fromPowerManagerSleepReason(wakefulnessLifecycle.lastSleepReason),
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
new file mode 100644
index 0000000..6791d88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.WakefulnessLifecycle
+
+enum class WakefulnessState {
+    /** The device is asleep and not interactive. */
+    ASLEEP,
+    /** Received a signal that the device is beginning to wake up. */
+    STARTING_TO_WAKE,
+    /** Device is now fully awake and interactive. */
+    AWAKE,
+    /** Signal that the device is now going to sleep. */
+    STARTING_TO_SLEEP;
+
+    companion object {
+        fun fromWakefulnessLifecycleInt(
+            @WakefulnessLifecycle.Wakefulness value: Int
+        ): WakefulnessState {
+            return when (value) {
+                WakefulnessLifecycle.WAKEFULNESS_ASLEEP -> ASLEEP
+                WakefulnessLifecycle.WAKEFULNESS_WAKING -> STARTING_TO_WAKE
+                WakefulnessLifecycle.WAKEFULNESS_AWAKE -> AWAKE
+                WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP -> STARTING_TO_SLEEP
+                else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 3276b6d..cbe512f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.binder
 
+import android.graphics.drawable.Animatable2
 import android.util.Size
 import android.util.TypedValue
 import android.view.View
@@ -27,12 +28,11 @@
 import androidx.core.view.updateLayoutParams
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.LockIconViewController
 import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
@@ -73,7 +73,8 @@
         fun onConfigurationChanged()
 
         /**
-         * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+         * Returns whether the keyguard bottom area should be constrained to the top of the lock
+         * icon
          */
         fun shouldConstrainToTopOfLockIcon(): Boolean
     }
@@ -217,7 +218,7 @@
             }
 
             override fun shouldConstrainToTopOfLockIcon(): Boolean =
-                    viewModel.shouldConstrainToTopOfLockIcon()
+                viewModel.shouldConstrainToTopOfLockIcon()
         }
     }
 
@@ -248,6 +249,27 @@
 
         IconViewBinder.bind(viewModel.icon, view)
 
+        (view.drawable as? Animatable2)?.let { animatable ->
+            (viewModel.icon as? Icon.Resource)?.res?.let { iconResourceId ->
+                // Always start the animation (we do call stop() below, if we need to skip it).
+                animatable.start()
+
+                if (view.tag != iconResourceId) {
+                    // Here when we haven't run the animation on a previous update.
+                    //
+                    // Save the resource ID for next time, so we know not to re-animate the same
+                    // animation again.
+                    view.tag = iconResourceId
+                } else {
+                    // Here when we've already done this animation on a previous update and want to
+                    // skip directly to the final frame of the animation to avoid running it.
+                    //
+                    // By calling stop after start, we go to the final frame of the animation.
+                    animatable.stop()
+                }
+            }
+        }
+
         view.isActivated = viewModel.isActivated
         view.drawable.setTint(
             Utils.getColorAttrDefaultColor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
new file mode 100644
index 0000000..f1da882
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.launch
+
+object LightRevealScrimViewBinder {
+    @JvmStatic
+    fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) {
+        revealScrim.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.CREATED) {
+                launch {
+                    viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount }
+                }
+
+                launch {
+                    viewModel.lightRevealEffect.collect { effect ->
+                        revealScrim.revealEffect = effect
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
new file mode 100644
index 0000000..a46d441
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.LightRevealScrimInteractor
+import com.android.systemui.statusbar.LightRevealEffect
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Models UI state for the light reveal scrim, which is used during screen on and off animations to
+ * draw a gradient that reveals/hides the contents of the screen.
+ */
+class LightRevealScrimViewModel @Inject constructor(interactor: LightRevealScrimInteractor) {
+    val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect
+    val revealAmount: Flow<Float> = interactor.revealAmount
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
index c27bfa3..bb04b6b4 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -30,10 +30,11 @@
  */
 interface Diffable<T> {
     /**
-     * Finds the differences between [prevVal] and [this] and logs those diffs to [row].
+     * Finds the differences between [prevVal] and this object and logs those diffs to [row].
      *
      * Each implementer should determine which individual fields have changed between [prevVal] and
-     * [this], and only log the fields that have actually changed. This helps save buffer space.
+     * this object, and only log the fields that have actually changed. This helps save buffer
+     * space.
      *
      * For example, if:
      * - prevVal = Object(val1=100, val2=200, val3=300)
@@ -42,6 +43,16 @@
      * Then only the val3 change should be logged.
      */
     fun logDiffs(prevVal: T, row: TableRowLogger)
+
+    /**
+     * Logs all the relevant fields of this object to [row].
+     *
+     * As opposed to [logDiffs], this method should log *all* fields.
+     *
+     * Implementation is optional. This method will only be used with [logDiffsForTable] in order to
+     * fully log the initial value of the flow.
+     */
+    fun logFull(row: TableRowLogger) {}
 }
 
 /**
@@ -57,8 +68,35 @@
     columnPrefix: String,
     initialValue: T,
 ): Flow<T> {
-    return this.pairwiseBy(initialValue) { prevVal, newVal ->
+    // Fully log the initial value to the table.
+    val getInitialValue = {
+        tableLogBuffer.logChange(columnPrefix) { row -> initialValue.logFull(row) }
+        initialValue
+    }
+    return this.pairwiseBy(getInitialValue) { prevVal: T, newVal: T ->
         tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal)
         newVal
     }
 }
+
+/**
+ * Each time the boolean flow is updated with a new value that's different from the previous value,
+ * logs the new value to the given [tableLogBuffer].
+ */
+fun Flow<Boolean>.logDiffsForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: Boolean,
+): Flow<Boolean> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(columnPrefix, columnName, initialValue)
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal, newVal: Boolean ->
+        if (prevVal != newVal) {
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal)
+        }
+        newVal
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 429637a..9d0b833 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,10 +16,7 @@
 
 package com.android.systemui.log.table
 
-import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.DumpsysTableLogger
 import com.android.systemui.plugins.util.RingBuffer
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
@@ -83,7 +80,7 @@
     maxSize: Int,
     private val name: String,
     private val systemClock: SystemClock,
-) {
+) : Dumpable {
     init {
         if (maxSize <= 0) {
             throw IllegalArgumentException("maxSize must be > 0")
@@ -104,6 +101,9 @@
      * @param columnPrefix a prefix that will be applied to every column name that gets logged. This
      * ensures that all the columns related to the same state object will be grouped together in the
      * table.
+     *
+     * @throws IllegalArgumentException if [columnPrefix] or column name contain "|". "|" is used as
+     * the separator token for parsing, so it can't be present in any part of the column name.
      */
     @Synchronized
     fun <T : Diffable<T>> logDiffs(columnPrefix: String, prevVal: T, newVal: T) {
@@ -113,6 +113,25 @@
         newVal.logDiffs(prevVal, row)
     }
 
+    /**
+     * Logs change(s) to the buffer using [rowInitializer].
+     *
+     * @param rowInitializer a function that will be called immediately to store relevant data on
+     * the row.
+     */
+    @Synchronized
+    fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) {
+        val row = tempRow
+        row.timestamp = systemClock.currentTimeMillis()
+        row.columnPrefix = columnPrefix
+        rowInitializer(row)
+    }
+
+    /** Logs a boolean change. */
+    fun logChange(prefix: String, columnName: String, value: Boolean) {
+        logChange(systemClock.currentTimeMillis(), prefix, columnName, value)
+    }
+
     // Keep these individual [logChange] methods private (don't let clients give us their own
     // timestamps.)
 
@@ -135,32 +154,31 @@
 
     @Synchronized
     private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
+        verifyValidName(prefix, columnName)
         val tableChange = buffer.advance()
         tableChange.reset(timestamp, prefix, columnName)
         return tableChange
     }
 
-    /**
-     * Registers this buffer as dumpables in [dumpManager]. Must be called for the table to be
-     * dumped.
-     *
-     * This will be automatically called in [TableLogBufferFactory.create].
-     */
-    fun registerDumpables(dumpManager: DumpManager) {
-        dumpManager.registerNormalDumpable("$name-changes", changeDumpable)
-        dumpManager.registerNormalDumpable("$name-table", tableDumpable)
+    private fun verifyValidName(prefix: String, columnName: String) {
+        if (prefix.contains(SEPARATOR)) {
+            throw IllegalArgumentException("columnPrefix cannot contain $SEPARATOR but was $prefix")
+        }
+        if (columnName.contains(SEPARATOR)) {
+            throw IllegalArgumentException(
+                "columnName cannot contain $SEPARATOR but was $columnName"
+            )
+        }
     }
 
-    private val changeDumpable = Dumpable { pw, _ -> dumpChanges(pw) }
-    private val tableDumpable = Dumpable { pw, _ -> dumpTable(pw) }
-
-    /** Dumps the list of [TableChange] objects. */
     @Synchronized
-    @VisibleForTesting
-    fun dumpChanges(pw: PrintWriter) {
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println(HEADER_PREFIX + name)
+        pw.println("version $VERSION")
         for (i in 0 until buffer.size) {
             buffer[i].dump(pw)
         }
+        pw.println(FOOTER_PREFIX + name)
     }
 
     /** Dumps an individual [TableChange]. */
@@ -170,70 +188,14 @@
         }
         val formattedTimestamp = TABLE_LOG_DATE_FORMAT.format(timestamp)
         pw.print(formattedTimestamp)
-        pw.print(" ")
+        pw.print(SEPARATOR)
         pw.print(this.getName())
-        pw.print("=")
+        pw.print(SEPARATOR)
         pw.print(this.getVal())
         pw.println()
     }
 
     /**
-     * Coalesces all the [TableChange] objects into a table of values of time and dumps the table.
-     */
-    // TODO(b/259454430): Since this is an expensive process, it could cause the bug report dump to
-    //   fail and/or not dump anything else. We should move this processing to ABT (Android Bug
-    //   Tool), where we have unlimited time to process.
-    @Synchronized
-    @VisibleForTesting
-    fun dumpTable(pw: PrintWriter) {
-        val messages = buffer.iterator().asSequence().toList()
-
-        if (messages.isEmpty()) {
-            return
-        }
-
-        // Step 1: Create list of column headers
-        val headerSet = mutableSetOf<String>()
-        messages.forEach { headerSet.add(it.getName()) }
-        val headers: MutableList<String> = headerSet.toList().sorted().toMutableList()
-        headers.add(0, "timestamp")
-
-        // Step 2: Create a list with the current values for each column. Will be updated with each
-        // change.
-        val currentRow: MutableList<String> = MutableList(headers.size) { DEFAULT_COLUMN_VALUE }
-
-        // Step 3: For each message, make the correct update to [currentRow] and save it to [rows].
-        val columnIndices: Map<String, Int> =
-            headers.mapIndexed { index, headerName -> headerName to index }.toMap()
-        val allRows = mutableListOf<List<String>>()
-
-        messages.forEach {
-            if (!it.hasData()) {
-                return@forEach
-            }
-
-            val formattedTimestamp = TABLE_LOG_DATE_FORMAT.format(it.timestamp)
-            if (formattedTimestamp != currentRow[0]) {
-                // The timestamp has updated, so save the previous row and continue to the next row
-                allRows.add(currentRow.toList())
-                currentRow[0] = formattedTimestamp
-            }
-            val columnIndex = columnIndices[it.getName()]!!
-            currentRow[columnIndex] = it.getVal()
-        }
-        // Add the last row
-        allRows.add(currentRow.toList())
-
-        // Step 4: Dump the rows
-        DumpsysTableLogger(
-                name,
-                headers,
-                allRows,
-            )
-            .printTableData(pw)
-    }
-
-    /**
      * A private implementation of [TableRowLogger].
      *
      * Used so that external clients can't modify [timestamp].
@@ -261,4 +223,8 @@
 }
 
 val TABLE_LOG_DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
-private const val DEFAULT_COLUMN_VALUE = "UNKNOWN"
+
+private const val HEADER_PREFIX = "SystemUI StateChangeTableSection START: "
+private const val FOOTER_PREFIX = "SystemUI StateChangeTableSection END: "
+private const val SEPARATOR = "|"
+private const val VERSION = "1"
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index f1f906f..7a90a74 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -34,7 +34,7 @@
         maxSize: Int,
     ): TableLogBuffer {
         val tableBuffer = TableLogBuffer(adjustMaxSize(maxSize), name, systemClock)
-        tableBuffer.registerDumpables(dumpManager)
+        dumpManager.registerNormalDumpable(name, tableBuffer)
         return tableBuffer
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
index 4891297..2d10b82 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
@@ -32,10 +32,12 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.Utils
 import com.android.systemui.util.time.SystemClock
@@ -55,6 +57,8 @@
 constructor(
     private val context: Context,
     private val broadcastDispatcher: BroadcastDispatcher,
+    private val userTracker: UserTracker,
+    @Main private val mainExecutor: Executor,
     @Background private val backgroundExecutor: Executor,
     private val tunerService: TunerService,
     private val mediaBrowserFactory: ResumeMediaBrowserFactory,
@@ -77,18 +81,26 @@
     private var currentUserId: Int = context.userId
 
     @VisibleForTesting
-    val userChangeReceiver =
+    val userUnlockReceiver =
         object : BroadcastReceiver() {
             override fun onReceive(context: Context, intent: Intent) {
                 if (Intent.ACTION_USER_UNLOCKED == intent.action) {
-                    loadMediaResumptionControls()
-                } else if (Intent.ACTION_USER_SWITCHED == intent.action) {
-                    currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
-                    loadSavedComponents()
+                    val userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
+                    if (userId == currentUserId) {
+                        loadMediaResumptionControls()
+                    }
                 }
             }
         }
 
+    private val userTrackerCallback =
+        object : UserTracker.Callback {
+            override fun onUserChanged(newUser: Int, userContext: Context) {
+                currentUserId = newUser
+                loadSavedComponents()
+            }
+        }
+
     private val mediaBrowserCallback =
         object : ResumeMediaBrowser.Callback() {
             override fun addTrack(
@@ -126,13 +138,13 @@
             dumpManager.registerDumpable(TAG, this)
             val unlockFilter = IntentFilter()
             unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
-            unlockFilter.addAction(Intent.ACTION_USER_SWITCHED)
             broadcastDispatcher.registerReceiver(
-                userChangeReceiver,
+                userUnlockReceiver,
                 unlockFilter,
                 null,
                 UserHandle.ALL
             )
+            userTracker.addCallback(userTrackerCallback, mainExecutor)
             loadSavedComponents()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 8aaee81..1fdbc99 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -184,6 +184,7 @@
 
     private val configListener =
         object : ConfigurationController.ConfigurationListener {
+
             override fun onDensityOrFontScaleChanged() {
                 // System font changes should only happen when UMO is offscreen or a flicker may
                 // occur
@@ -199,6 +200,7 @@
             override fun onConfigChanged(newConfig: Configuration?) {
                 if (newConfig == null) return
                 isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
+                updatePlayers(recreateMedia = true)
             }
 
             override fun onUiModeChanged() {
@@ -635,7 +637,7 @@
             val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
             existingSmartspaceMediaKey?.let {
                 val removedPlayer =
-                    MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey, true)
+                    removePlayer(existingSmartspaceMediaKey, dismissMediaData = false)
                 removedPlayer?.run {
                     debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey)
                 }
@@ -685,7 +687,7 @@
         key: String,
         dismissMediaData: Boolean = true,
         dismissRecommendation: Boolean = true
-    ) {
+    ): MediaControlPanel? {
         if (key == MediaPlayerData.smartspaceMediaKey()) {
             MediaPlayerData.smartspaceMediaData?.let {
                 logger.logRecommendationRemoved(it.packageName, it.instanceId)
@@ -693,7 +695,7 @@
         }
         val removed =
             MediaPlayerData.removeMediaPlayer(key, dismissMediaData || dismissRecommendation)
-        removed?.apply {
+        return removed?.apply {
             mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
             mediaContent.removeView(removed.mediaViewHolder?.player)
             mediaContent.removeView(removed.recommendationViewHolder?.recommendations)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 21e64e2..827ac78 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -458,7 +458,9 @@
         if (mMediaViewHolder == null) {
             return;
         }
-        Trace.beginSection("MediaControlPanel#bindPlayer<" + key + ">");
+        if (Trace.isEnabled()) {
+            Trace.traceBegin(Trace.TRACE_TAG_APP, "MediaControlPanel#bindPlayer<" + key + ">");
+        }
         mKey = key;
         mMediaData = data;
         MediaSession.Token token = data.getToken();
@@ -1179,8 +1181,10 @@
             return;
         }
 
-        Trace.beginSection(
-                "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+        if (Trace.isEnabled()) {
+            Trace.traceBegin(Trace.TRACE_TAG_APP,
+                    "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+        }
 
         mRecommendationData = data;
         mSmartspaceId = SmallHash.hash(data.getTargetId());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d132a95..5202562 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -57,13 +57,12 @@
 import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
 
 import android.annotation.IdRes;
+import android.annotation.NonNull;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
@@ -120,7 +119,6 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -138,6 +136,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.shared.recents.utilities.Utilities;
@@ -208,7 +207,7 @@
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final OverviewProxyService mOverviewProxyService;
     private final NavigationModeController mNavigationModeController;
-    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final UserTracker mUserTracker;
     private final CommandQueue mCommandQueue;
     private final Optional<Pip> mPipOptional;
     private final Optional<Recents> mRecentsOptional;
@@ -516,7 +515,7 @@
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             SysUiState sysUiFlagsContainer,
-            BroadcastDispatcher broadcastDispatcher,
+            UserTracker userTracker,
             CommandQueue commandQueue,
             Optional<Pip> pipOptional,
             Optional<Recents> recentsOptional,
@@ -559,7 +558,7 @@
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mOverviewProxyService = overviewProxyService;
         mNavigationModeController = navigationModeController;
-        mBroadcastDispatcher = broadcastDispatcher;
+        mUserTracker = userTracker;
         mCommandQueue = commandQueue;
         mPipOptional = pipOptional;
         mRecentsOptional = recentsOptional;
@@ -745,9 +744,7 @@
         prepareNavigationBarView();
         checkNavBarModes();
 
-        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
-                Handler.getMain(), UserHandle.ALL);
+        mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         notifyNavigationBarScreenOn();
 
@@ -798,7 +795,7 @@
         mView.setUpdateActiveTouchRegionsCallback(null);
         getBarTransitions().destroy();
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
-        mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+        mUserTracker.removeCallback(mUserChangedCallback);
         mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
         if (mOrientationHandle != null) {
             resetSecondaryHandle();
@@ -1743,21 +1740,14 @@
         }
     };
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            // TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is
-            // async, but we've already cleared the fields. Just return early in this case.
-            if (mView == null) {
-                return;
-            }
-            String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                // The accessibility settings may be different for the new user
-                updateAccessibilityStateFlags();
-            }
-        }
-    };
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    // The accessibility settings may be different for the new user
+                    updateAccessibilityStateFlags();
+                }
+            };
 
     @VisibleForTesting
     int getNavigationIconHints() {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1da866e..5a1ad96 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -39,6 +39,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.fuelgauge.Estimate;
 import com.android.settingslib.utils.ThreadUtils;
@@ -47,6 +49,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -80,6 +83,7 @@
     private final PowerManager mPowerManager;
     private final WarningsUI mWarnings;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final UserTracker mUserTracker;
     private InattentiveSleepWarningView mOverlayView;
     private final Configuration mLastConfiguration = new Configuration();
     private int mPlugType = 0;
@@ -122,12 +126,21 @@
                 }
             };
 
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    mWarnings.userSwitched();
+                }
+            };
+
     @Inject
     public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
             CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
             WakefulnessLifecycle wakefulnessLifecycle,
-            PowerManager powerManager) {
+            PowerManager powerManager,
+            UserTracker userTracker) {
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
         mCommandQueue = commandQueue;
@@ -136,6 +149,7 @@
         mEnhancedEstimates = enhancedEstimates;
         mPowerManager = powerManager;
         mWakefulnessLifecycle = wakefulnessLifecycle;
+        mUserTracker = userTracker;
     }
 
     public void start() {
@@ -154,6 +168,7 @@
                 false, obs, UserHandle.USER_ALL);
         updateBatteryWarningLevels();
         mReceiver.init();
+        mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
 
         // Check to see if we need to let the user know that the phone previously shut down due
@@ -250,7 +265,6 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
             mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
             // Force get initial values. Relying on Sticky behavior until API for getting info.
             if (!mHasReceivedBattery) {
@@ -332,8 +346,6 @@
                             plugged, bucket);
                 });
 
-            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                mWarnings.userSwitched();
             } else {
                 Slog.w(TAG, "unknown intent: " + intent);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 314252b..4c9c99c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.qs.QSUserSwitcherEvent
 import com.android.systemui.qs.tiles.UserDetailView
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.user.ui.dialog.DialogShowerImpl
 import javax.inject.Inject
 import javax.inject.Provider
 
@@ -130,19 +131,6 @@
         }
     }
 
-    private class DialogShowerImpl(
-        private val animateFrom: Dialog,
-        private val dialogLaunchAnimator: DialogLaunchAnimator
-    ) : DialogInterface by animateFrom, DialogShower {
-        override fun showDialog(dialog: Dialog, cuj: DialogCuj) {
-            dialogLaunchAnimator.showFromDialog(
-                dialog,
-                animateFrom = animateFrom,
-                cuj
-            )
-        }
-    }
-
     interface DialogShower : DialogInterface {
         fun showDialog(dialog: Dialog, cuj: DialogCuj)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 2ee5f05..645b125 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -51,10 +51,13 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.util.leak.RotationUtils;
@@ -76,6 +79,7 @@
     private final AccessibilityManager mAccessibilityService;
     private final WindowManager mWindowManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final UserTracker mUserTracker;
 
     private RequestWindowView mRequestWindow;
     private int mNavBarMode;
@@ -83,12 +87,21 @@
     /** ID of task to be pinned or locked. */
     private int taskId;
 
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    clearPrompt();
+                }
+            };
+
     @Inject
     public ScreenPinningRequest(
             Context context,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             NavigationModeController navigationModeController,
-            BroadcastDispatcher broadcastDispatcher) {
+            BroadcastDispatcher broadcastDispatcher,
+            UserTracker userTracker) {
         mContext = context;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
         mAccessibilityService = (AccessibilityManager)
@@ -97,6 +110,7 @@
                 mContext.getSystemService(Context.WINDOW_SERVICE);
         mNavBarMode = navigationModeController.addListener(this);
         mBroadcastDispatcher = broadcastDispatcher;
+        mUserTracker = userTracker;
     }
 
     public void clearPrompt() {
@@ -228,9 +242,9 @@
             }
 
             IntentFilter filter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
             filter.addAction(Intent.ACTION_SCREEN_OFF);
             mBroadcastDispatcher.registerReceiver(mReceiver, filter);
+            mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
         }
 
         private void inflateView(int rotation) {
@@ -358,6 +372,7 @@
         @Override
         public void onDetachedFromWindow() {
             mBroadcastDispatcher.unregisterReceiver(mReceiver);
+            mUserTracker.removeCallback(mUserChangedCallback);
         }
 
         protected void onConfigurationChanged() {
@@ -388,8 +403,7 @@
             public void onReceive(Context context, Intent intent) {
                 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                     post(mUpdateLayoutRunnable);
-                } else if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)
-                        || intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+                } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                     clearPrompt();
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ce4e0ec..b8684ee 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -33,13 +33,16 @@
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.CallbackController;
 
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -55,8 +58,10 @@
     private boolean mIsRecording;
     private PendingIntent mStopIntent;
     private CountDownTimer mCountDownTimer = null;
-    private BroadcastDispatcher mBroadcastDispatcher;
-    private UserContextProvider mUserContextProvider;
+    private final Executor mMainExecutor;
+    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final UserContextProvider mUserContextProvider;
+    private final UserTracker mUserTracker;
 
     protected static final String INTENT_UPDATE_STATE =
             "com.android.systemui.screenrecord.UPDATE_STATE";
@@ -66,12 +71,13 @@
             new CopyOnWriteArrayList<>();
 
     @VisibleForTesting
-    protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            stopRecording();
-        }
-    };
+    final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    stopRecording();
+                }
+            };
 
     @VisibleForTesting
     protected final BroadcastReceiver mStateChangeReceiver = new BroadcastReceiver() {
@@ -92,10 +98,14 @@
      * Create a new RecordingController
      */
     @Inject
-    public RecordingController(BroadcastDispatcher broadcastDispatcher,
-            UserContextProvider userContextProvider) {
+    public RecordingController(@Main Executor mainExecutor,
+            BroadcastDispatcher broadcastDispatcher,
+            UserContextProvider userContextProvider,
+            UserTracker userTracker) {
+        mMainExecutor = mainExecutor;
         mBroadcastDispatcher = broadcastDispatcher;
         mUserContextProvider = userContextProvider;
+        mUserTracker = userTracker;
     }
 
     /** Create a dialog to show screen recording options to the user. */
@@ -139,9 +149,7 @@
                 }
                 try {
                     startIntent.send();
-                    IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-                    mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null,
-                            UserHandle.ALL);
+                    mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
 
                     IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE);
                     mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null,
@@ -211,7 +219,7 @@
     public synchronized void updateState(boolean isRecording) {
         if (!isRecording && mIsRecording) {
             // Unregister receivers if we have stopped recording
-            mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver);
+            mUserTracker.removeCallback(mUserChangedCallback);
             mBroadcastDispatcher.unregisterReceiver(mStateChangeReceiver);
         }
         mIsRecording = isRecording;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 57b256e..a6447a5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -579,9 +579,16 @@
 
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
-        withWindowAttached(() ->
+        withWindowAttached(() -> {
+            if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+                    && mUserManager.isManagedProfile(owner.getIdentifier())) {
+                mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+                        R.string.screenshot_saving_work_profile_title));
+            } else {
                 mScreenshotView.announceForAccessibility(
-                        mContext.getResources().getString(R.string.screenshot_saving_title)));
+                        mContext.getResources().getString(R.string.screenshot_saving_title));
+            }
+        });
 
         mScreenshotView.reset();
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 7641554..fae938d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -825,12 +825,23 @@
             }
         });
         if (mQuickShareChip != null) {
-            mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
-                    () -> {
-                        mUiEventLogger.log(
-                                ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0, mPackageName);
-                        animateDismissal();
-                    });
+            if (imageData.quickShareAction != null) {
+                mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
+                        () -> {
+                            mUiEventLogger.log(
+                                    ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0,
+                                    mPackageName);
+                            animateDismissal();
+                        });
+            } else {
+                // hide chip and unset pending interaction if necessary, since we don't actually
+                // have a useable quick share intent
+                Log.wtf(TAG, "Showed quick share chip, but quick share intent was null");
+                if (mPendingInteraction == PendingInteraction.QUICK_SHARE) {
+                    mPendingInteraction = null;
+                }
+                mQuickShareChip.setVisibility(GONE);
+            }
         }
 
         if (mPendingInteraction != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7fbdeca..60376f4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -393,6 +393,9 @@
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private int mQsTrackingPointer;
     private VelocityTracker mQsVelocityTracker;
+    private TrackingStartedListener mTrackingStartedListener;
+    private OpenCloseListener mOpenCloseListener;
+    private GestureRecorder mGestureRecorder;
     private boolean mQsTracking;
     /** Whether the ongoing gesture might both trigger the expansion in both the view and QS. */
     private boolean mConflictingQsExpansionGesture;
@@ -1362,6 +1365,14 @@
         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
     }
 
+    void setOpenCloseListener(OpenCloseListener openCloseListener) {
+        mOpenCloseListener = openCloseListener;
+    }
+
+    void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
+        mTrackingStartedListener = trackingStartedListener;
+    }
+
     private void updateGestureExclusionRect() {
         Rect exclusionRect = calculateGestureExclusionRect();
         mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList()
@@ -1936,9 +1947,9 @@
     }
 
     private void fling(float vel) {
-        GestureRecorder gr = mCentralSurfaces.getGestureRecorder();
-        if (gr != null) {
-            gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
+        if (mGestureRecorder != null) {
+            mGestureRecorder.tag("fling " + ((vel > 0) ? "open" : "closed"),
+                    "notifications,v=" + vel);
         }
         fling(vel, true, 1.0f /* collapseSpeedUpFactor */, false);
     }
@@ -2072,6 +2083,14 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
+                float qsExpansionFraction = computeQsExpansionFraction();
+                // Intercept the touch if QS is between fully collapsed and fully expanded state
+                if (!mSplitShadeEnabled
+                        && qsExpansionFraction > 0.0 && qsExpansionFraction < 1.0) {
+                    mShadeLog.logMotionEvent(event,
+                            "onQsIntercept: down action, QS partially expanded/collapsed");
+                    return true;
+                }
                 if (mKeyguardShowing
                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
                     // Dragging down on the lockscreen statusbar should prohibit other interactions
@@ -2324,6 +2343,14 @@
         if (!isFullyCollapsed()) {
             handleQsDown(event);
         }
+        // defer touches on QQS to shade while shade is collapsing. Added margin for error
+        // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS.
+        if (!mSplitShadeEnabled
+                && computeQsExpansionFraction() <= 0.01 && getExpandedFraction() < 1.0) {
+            mShadeLog.logMotionEvent(event,
+                    "handleQsTouch: QQS touched while shade collapsing");
+            mQsTracking = false;
+        }
         if (!mQsExpandImmediate && mQsTracking) {
             onQsTouch(event);
             if (!mConflictingQsExpansionGesture && !mSplitShadeEnabled) {
@@ -2564,7 +2591,6 @@
         // Reset scroll position and apply that position to the expanded height.
         float height = mQsExpansionHeight;
         setQsExpansionHeight(height);
-        updateExpandedHeightToMaxHeight();
         mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
 
         // When expanding QS, let's authenticate the user if possible,
@@ -3711,7 +3737,7 @@
         mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
         endClosing();
         mTracking = true;
-        mCentralSurfaces.onTrackingStarted();
+        mTrackingStartedListener.onTrackingStarted();
         notifyExpandingStarted();
         updatePanelExpansionAndVisibility();
         mScrimController.onTrackingStarted();
@@ -3945,7 +3971,7 @@
     }
 
     private void onClosingFinished() {
-        mCentralSurfaces.onClosingFinished();
+        mOpenCloseListener.onClosingFinished();
         setClosingWithAlphaFadeout(false);
         mMediaHierarchyManager.closeGuts();
     }
@@ -4504,11 +4530,13 @@
      */
     public void initDependencies(
             CentralSurfaces centralSurfaces,
+            GestureRecorder recorder,
             Runnable hideExpandedRunnable,
             NotificationShelfController notificationShelfController) {
         // TODO(b/254859580): this can be injected.
         mCentralSurfaces = centralSurfaces;
 
+        mGestureRecorder = recorder;
         mHideExpandedRunnable = hideExpandedRunnable;
         mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
         mNotificationShelfController = notificationShelfController;
@@ -4590,20 +4618,20 @@
                         return false;
                     }
 
-                    // If the view that would receive the touch is disabled, just have status bar
-                    // eat the gesture.
-                    if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) {
-                        Log.v(TAG,
-                                String.format(
-                                        "onTouchForwardedFromStatusBar: "
-                                                + "panel view disabled, eating touch at (%d,%d)",
-                                        (int) event.getX(),
-                                        (int) event.getY()
-                                )
-                        );
-                        return true;
+                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                        // If the view that would receive the touch is disabled, just have status
+                        // bar eat the gesture.
+                        if (!mView.isEnabled()) {
+                            mShadeLog.logMotionEvent(event,
+                                    "onTouchForwardedFromStatusBar: panel view disabled");
+                            return true;
+                        }
+                        if (isFullyCollapsed() && event.getY() < 1f) {
+                            // b/235889526 Eat events on the top edge of the phone when collapsed
+                            mShadeLog.logMotionEvent(event, "top edge touch ignored");
+                            return true;
+                        }
                     }
-
                     return mView.dispatchTouchEvent(event);
                 }
             };
@@ -5757,7 +5785,7 @@
             if (mSplitShadeEnabled && !isOnKeyguard()) {
                 setQsExpandImmediate(true);
             }
-            mCentralSurfaces.makeExpandedVisible(false);
+            mOpenCloseListener.onOpenStarted();
         }
         if (state == STATE_CLOSED) {
             setQsExpandImmediate(false);
@@ -6240,5 +6268,18 @@
             return super.performAccessibilityAction(host, action, args);
         }
     }
+
+    /** Listens for when touch tracking begins. */
+    interface TrackingStartedListener {
+        void onTrackingStarted();
+    }
+
+    /** Listens for when shade begins opening of finishes closing. */
+    interface OpenCloseListener {
+        /** Called when the shade finishes closing. */
+        void onClosingFinished();
+        /** Called when the shade starts opening. */
+        void onOpenStarted();
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index de9dcf9..a41a15d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -111,6 +111,9 @@
     /** Handle status bar touch event. */
     void onStatusBarTouch(MotionEvent event);
 
+    /** Called when the shade finishes collapsing. */
+    void onClosingFinished();
+
     /** Sets the listener for when the visibility of the shade changes. */
     void setVisibilityListener(ShadeVisibilityListener listener);
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index 807e2e6..638e748 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -227,6 +227,16 @@
     }
 
     @Override
+    public void onClosingFinished() {
+        runPostCollapseRunnables();
+        if (!mPresenter.isPresenterFullyCollapsed()) {
+            // if we set it not to be focusable when collapsing, we have to undo it when we aborted
+            // the closing
+            mNotificationShadeWindowController.setNotificationShadeFocusable(true);
+        }
+    }
+
+    @Override
     public void instantCollapseShade() {
         mNotificationPanelViewController.instantCollapse();
         runPostCollapseRunnables();
@@ -329,5 +339,18 @@
     public void setNotificationPanelViewController(
             NotificationPanelViewController notificationPanelViewController) {
         mNotificationPanelViewController = notificationPanelViewController;
+        mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables);
+        mNotificationPanelViewController.setOpenCloseListener(
+                new NotificationPanelViewController.OpenCloseListener() {
+                    @Override
+                    public void onClosingFinished() {
+                        ShadeControllerImpl.this.onClosingFinished();
+                    }
+
+                    @Override
+                    public void onOpenStarted() {
+                        makeExpandedVisible(false);
+                    }
+                });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index bc456d5..2334a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -15,6 +15,7 @@
 import android.util.AttributeSet
 import android.util.MathUtils.lerp
 import android.view.View
+import android.view.animation.PathInterpolator
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
 import com.android.systemui.util.getColorWithAlpha
@@ -88,10 +89,12 @@
 
 class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
 
-    private val INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+    // Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster
+    private val interpolator = PathInterpolator(/* controlX1= */ 0.4f, /* controlY1= */ 0f,
+            /* controlX2= */ 0.2f, /* controlY2= */ 1f)
 
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
-        val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
+        val interpolatedAmount = interpolator.getInterpolation(amount)
 
         scrim.interpolatedRevealAmount = interpolatedAmount
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index cdefae6..f4cd985 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -30,6 +30,7 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -37,6 +38,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.statusbar.NotificationVisibility;
@@ -127,21 +129,6 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             switch (action) {
-                case Intent.ACTION_USER_SWITCHED:
-                    mCurrentUserId = intent.getIntExtra(
-                            Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
-                    updateCurrentProfilesCache();
-
-                    Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
-
-                    updateLockscreenNotificationSetting();
-                    updatePublicMode();
-                    mPresenter.onUserSwitched(mCurrentUserId);
-
-                    for (UserChangedListener listener : mListeners) {
-                        listener.onUserChanged(mCurrentUserId);
-                    }
-                    break;
                 case Intent.ACTION_USER_REMOVED:
                     int removedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                     if (removedUserId != -1) {
@@ -181,6 +168,25 @@
         }
     };
 
+    protected final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    mCurrentUserId = newUser;
+                    updateCurrentProfilesCache();
+
+                    Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+
+                    updateLockscreenNotificationSetting();
+                    updatePublicMode();
+                    mPresenter.onUserSwitched(mCurrentUserId);
+
+                    for (UserChangedListener listener : mListeners) {
+                        listener.onUserChanged(mCurrentUserId);
+                    }
+                }
+            };
+
     protected final Context mContext;
     private final Handler mMainHandler;
     protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
@@ -284,7 +290,6 @@
                 null /* handler */, UserHandle.ALL);
 
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
@@ -298,6 +303,8 @@
         mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null,
                 Context.RECEIVER_EXPORTED_UNAUDITED);
 
+        mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler));
+
         mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 7eb8906..39daa13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -44,4 +44,8 @@
     val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
         featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
     }
+
+    val isNoHunForOldWhenEnabled: Boolean by lazy {
+        featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index d97b712..3e2dd05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -38,6 +38,7 @@
 import javax.inject.Inject
 import kotlin.math.min
 
+
 @SysUISingleton
 class NotificationWakeUpCoordinator @Inject constructor(
     dumpManager: DumpManager,
@@ -45,7 +46,8 @@
     private val statusBarStateController: StatusBarStateController,
     private val bypassController: KeyguardBypassController,
     private val dozeParameters: DozeParameters,
-    private val screenOffAnimationController: ScreenOffAnimationController
+    private val screenOffAnimationController: ScreenOffAnimationController,
+    private val logger: NotificationWakeUpCoordinatorLogger,
 ) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, ShadeExpansionListener,
     Dumpable {
 
@@ -242,6 +244,7 @@
     }
 
     override fun onDozeAmountChanged(linear: Float, eased: Float) {
+        logger.logOnDozeAmountChanged(linear, eased)
         if (overrideDozeAmountIfAnimatingScreenOff(linear)) {
             return
         }
@@ -273,6 +276,7 @@
     }
 
     override fun onStateChanged(newState: Int) {
+        logger.logOnStateChanged(newState)
         if (state == StatusBarState.SHADE && newState == StatusBarState.SHADE) {
             // The SHADE -> SHADE transition is only possible as part of cancelling the screen-off
             // animation (e.g. by fingerprint unlock).  This is done because the system is in an
@@ -320,8 +324,12 @@
     private fun overrideDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
             if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+                logger.logSetDozeAmount("1.0", "1.0",
+                        "Override: bypass (keyguard)", StatusBarState.KEYGUARD)
                 setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
             } else {
+                logger.logSetDozeAmount("0.0", "0.0",
+                        "Override: bypass (shade)", statusBarStateController.state)
                 setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
             }
             return true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
new file mode 100644
index 0000000..b40ce25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.DEBUG
+import javax.inject.Inject
+
+class NotificationWakeUpCoordinatorLogger
+@Inject
+constructor(@NotificationLog private val buffer: LogBuffer) {
+    fun logSetDozeAmount(linear: String, eased: String, source: String, state: Int) {
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = linear
+                str2 = eased
+                str3 = source
+                int1 = state
+            },
+            { "setDozeAmount: linear: $str1, eased: $str2, source: $str3, state: $int1" }
+        )
+    }
+
+    fun logOnDozeAmountChanged(linear: Float, eased: Float) {
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                double1 = linear.toDouble()
+                str2 = eased.toString()
+            },
+            { "onDozeAmountChanged($double1, $str2)" }
+        )
+    }
+
+    fun logOnStateChanged(newState: Int) {
+        buffer.log(TAG, DEBUG, { int1 = newState }, { "onStateChanged($int1)" })
+    }
+}
+
+private const val TAG = "NotificationWakeUpCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
index 966ab4c..afdadeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -51,7 +51,9 @@
      */
     public final void invalidateList(@Nullable String reason) {
         if (mListener != null) {
-            Trace.beginSection("Pluggable<" + mName + ">.invalidateList");
+            if (Trace.isEnabled()) {
+                Trace.traceBegin(Trace.TRACE_TAG_APP, "Pluggable<" + mName + ">.invalidateList");
+            }
             mListener.onPluggableInvalidated((This) this, reason);
             Trace.endSection();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index e6dbcee..7513aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -2,22 +2,20 @@
 
 import android.app.Notification
 import android.app.Notification.VISIBILITY_SECRET
-import android.content.BroadcastReceiver
 import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
 import android.database.ContentObserver
 import android.net.Uri
 import android.os.Handler
+import android.os.HandlerExecutor
 import android.os.UserHandle
 import android.provider.Settings
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.CoreStartable
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -78,7 +76,7 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val highPriorityProvider: HighPriorityProvider,
     private val statusBarStateController: SysuiStatusBarStateController,
-    private val broadcastDispatcher: BroadcastDispatcher,
+    private val userTracker: UserTracker,
     private val secureSettings: SecureSettings,
     private val globalSettings: GlobalSettings
 ) : CoreStartable, KeyguardNotificationVisibilityProvider {
@@ -87,6 +85,15 @@
     private val onStateChangedListeners = ListenerSet<Consumer<String>>()
     private var hideSilentNotificationsOnLockscreen: Boolean = false
 
+    private val userTrackerCallback = object : UserTracker.Callback {
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            if (isLockedOrLocking) {
+                // maybe public mode changed
+                notifyStateChanged("onUserSwitched")
+            }
+        }
+    }
+
     override fun start() {
         readShowSilentNotificationSetting()
         keyguardStateController.addCallback(object : KeyguardStateController.Callback {
@@ -143,14 +150,7 @@
                 notifyStateChanged("onStatusBarUpcomingStateChanged")
             }
         })
-        broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
-            override fun onReceive(context: Context, intent: Intent) {
-                if (isLockedOrLocking) {
-                    // maybe public mode changed
-                    notifyStateChanged(intent.action!!)
-                }
-            }
-        }, IntentFilter(Intent.ACTION_USER_SWITCHED))
+        userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
     }
 
     override fun addOnStateChangedListener(listener: Consumer<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 073b6b0..13b3aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -106,6 +106,36 @@
         })
     }
 
+    fun logNoHeadsUpOldWhen(
+        entry: NotificationEntry,
+        notifWhen: Long,
+        notifAge: Long
+    ) {
+        buffer.log(TAG, DEBUG, {
+            str1 = entry.logKey
+            long1 = notifWhen
+            long2 = notifAge
+        }, {
+            "No heads up: old when $long1 (age=$long2 ms): $str1"
+        })
+    }
+
+    fun logMaybeHeadsUpDespiteOldWhen(
+        entry: NotificationEntry,
+        notifWhen: Long,
+        notifAge: Long,
+        reason: String
+    ) {
+        buffer.log(TAG, DEBUG, {
+            str1 = entry.logKey
+            str2 = reason
+            long1 = notifWhen
+            long2 = notifAge
+        }, {
+            "Maybe heads up: old when $long1 (age=$long2 ms) but $str2: $str1"
+        })
+    }
+
     fun logNoHeadsUpSuppressedBy(
         entry: NotificationEntry,
         suppressor: NotificationInterruptSuppressor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index c4f5a3a..ec5bd68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR;
 
+import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
@@ -82,7 +83,10 @@
         FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(1235),
 
         @UiEvent(doc = "FSI suppressed for requiring neither HUN nor keyguard")
-        FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236);
+        FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),
+
+        @UiEvent(doc = "HUN suppressed for old when")
+        HUN_SUPPRESSED_OLD_WHEN(1237);
 
         private final int mId;
 
@@ -346,6 +350,10 @@
             return false;
         }
 
+        if (shouldSuppressHeadsUpWhenAwakeForOldWhen(entry, log)) {
+            return false;
+        }
+
         for (int i = 0; i < mSuppressors.size(); i++) {
             if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
                 if (log) mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
@@ -470,4 +478,51 @@
     private boolean isSnoozedPackage(StatusBarNotification sbn) {
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
+
+    private boolean shouldSuppressHeadsUpWhenAwakeForOldWhen(NotificationEntry entry, boolean log) {
+        if (!mFlags.isNoHunForOldWhenEnabled()) {
+            return false;
+        }
+
+        final Notification notification = entry.getSbn().getNotification();
+        if (notification == null) {
+            return false;
+        }
+
+        final long when = notification.when;
+        final long now = System.currentTimeMillis();
+        final long age = now - when;
+
+        if (age < MAX_HUN_WHEN_AGE_MS) {
+            return false;
+        }
+
+        if (when <= 0) {
+            // Some notifications (including many system notifications) are posted with the "when"
+            // field set to 0. Nothing in the Javadocs for Notification mentions a special meaning
+            // for a "when" of 0, but Android didn't even exist at the dawn of the Unix epoch.
+            // Therefore, assume that these notifications effectively don't have a "when" value,
+            // and don't suppress HUNs.
+            if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "when <= 0");
+            return false;
+        }
+
+        if (notification.fullScreenIntent != null) {
+            if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "full-screen intent");
+            return false;
+        }
+
+        if (notification.isForegroundService()) {
+            if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "foreground service");
+            return false;
+        }
+
+        if (log) mLogger.logNoHeadsUpOldWhen(entry, when, age);
+        final int uid = entry.getSbn().getUid();
+        final String packageName = entry.getSbn().getPackageName();
+        mUiEventLogger.log(NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN, uid, packageName);
+        return true;
+    }
+
+    public static final long MAX_HUN_WHEN_AGE_MS = 24 * 60 * 60 * 1000;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1eccc98..d7d5ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -19,6 +19,7 @@
 import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
 
 import android.animation.Animator;
@@ -1404,6 +1405,11 @@
         mKeepInParentForDismissAnimation = keepInParent;
     }
 
+    /** @return true if the User has dismissed this notif's parent */
+    public boolean isParentDismissed() {
+        return getEntry().getDismissState() == PARENT_DISMISSED;
+    }
+
     @Override
     public boolean isRemoved() {
         return mRemoved;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index f9e9a2d..8a400d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -359,10 +359,15 @@
 
     @Override
     public boolean offerToKeepInParentForAnimation() {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)) {
+        //If the User dismissed the notification's parent, we want to keep it attached until the
+        //dismiss animation is ongoing. Therefore we don't want to remove it in the ShadeViewDiffer.
+        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)
+                && mView.isParentDismissed()) {
             mView.setKeepInParentForDismissAnimation(true);
             return true;
         }
+
+        //Otherwise the view system doesn't do the removal, so we rely on the ShadeViewDiffer
         return false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 34e62ce..03057a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -496,7 +496,7 @@
                 && mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
     }
 
-    public int getMode() {
+    public @WakeAndUnlockMode int getMode() {
         return mMode;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 0ec7c62..e068f87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -51,7 +51,6 @@
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.LightRevealScrim;
 import com.android.systemui.statusbar.NotificationPresenter;
 
@@ -288,8 +287,6 @@
 
     void onTouchEvent(MotionEvent event);
 
-    GestureRecorder getGestureRecorder();
-
     BiometricUnlockController getBiometricUnlockController();
 
     void showWirelessChargingAnimation(int batteryLevel);
@@ -404,10 +401,6 @@
 
     LightRevealScrim getLightRevealScrim();
 
-    void onTrackingStarted();
-
-    void onClosingFinished();
-
     // TODO: Figure out way to remove these.
     NavigationBarView getNavigationBarView();
 
@@ -491,13 +484,6 @@
 
     void updateNotificationPanelTouchState();
 
-    /**
-     * TODO(b/257041702) delete this
-     * @deprecated Use ShadeController#makeExpandedVisible
-     */
-    @Deprecated
-    void makeExpandedVisible(boolean force);
-
     int getDisplayId();
 
     int getRotation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index d988772..d027ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -158,6 +158,8 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -474,6 +476,7 @@
     private final OngoingCallController mOngoingCallController;
     private final StatusBarSignalPolicy mStatusBarSignalPolicy;
     private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+    private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
 
     /** Controller for the Shade. */
     @VisibleForTesting
@@ -740,7 +743,8 @@
             DeviceStateManager deviceStateManager,
             WiredChargingRippleController wiredChargingRippleController,
             IDreamManager dreamManager,
-            Lazy<CameraLauncher> cameraLauncherLazy) {
+            Lazy<CameraLauncher> cameraLauncherLazy,
+            Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy) {
         mContext = context;
         mNotificationsController = notificationsController;
         mFragmentService = fragmentService;
@@ -854,6 +858,8 @@
         deviceStateManager.registerCallback(mMainExecutor,
                 new FoldStateListener(mContext, this::onFoldedStateChanged));
         wiredChargingRippleController.registerCallbacks();
+
+        mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
     }
 
     @Override
@@ -983,6 +989,12 @@
 
             @Override
             public void onKeyguardGoingAwayChanged() {
+                if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+                    // This code path is not used if the KeyguardTransitionRepository is managing
+                    // the lightreveal scrim.
+                    return;
+                }
+
                 // The light reveal scrim should always be fully revealed by the time the keyguard
                 // is done going away. Double check that this is true.
                 if (!mKeyguardStateController.isKeyguardGoingAway()) {
@@ -1219,6 +1231,12 @@
         mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
+
+        if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+            LightRevealScrimViewBinder.bind(
+                    mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
+        }
+
         mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
             Runnable updateOpaqueness = () -> {
                 mNotificationShadeWindowController.setLightRevealScrimOpaque(
@@ -1240,6 +1258,7 @@
 
         mNotificationPanelViewController.initDependencies(
                 this,
+                mGestureRec,
                 mShadeController::makeExpandedInvisible,
                 mNotificationShelfController);
 
@@ -1837,7 +1856,7 @@
     public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
         if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
                 && isLaunchForActivity) {
-            onClosingFinished();
+            mShadeController.onClosingFinished();
         } else {
             mShadeController.collapseShade(true /* animate */);
         }
@@ -1847,7 +1866,7 @@
     @Override
     public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
         if (!mPresenter.isCollapsing()) {
-            onClosingFinished();
+            mShadeController.onClosingFinished();
         }
         if (launchIsFullScreen) {
             mShadeController.instantCollapseShade();
@@ -2032,11 +2051,6 @@
     }
 
     @Override
-    public GestureRecorder getGestureRecorder() {
-        return mGestureRec;
-    }
-
-    @Override
     public BiometricUnlockController getBiometricUnlockController() {
         return mBiometricUnlockController;
     }
@@ -3293,6 +3307,10 @@
             return;
         }
 
+        if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+            return;
+        }
+
         final boolean wakingUpFromPowerButton = wakingUp
                 && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
                 && mWakefulnessLifecycle.getLastWakeReason()
@@ -3319,21 +3337,6 @@
         return mLightRevealScrim;
     }
 
-    @Override
-    public void onTrackingStarted() {
-        mShadeController.runPostCollapseRunnables();
-    }
-
-    @Override
-    public void onClosingFinished() {
-        mShadeController.runPostCollapseRunnables();
-        if (!mPresenter.isPresenterFullyCollapsed()) {
-            // if we set it not to be focusable when collapsing, we have to undo it when we aborted
-            // the closing
-            mNotificationShadeWindowController.setNotificationShadeFocusable(true);
-        }
-    }
-
     // TODO: Figure out way to remove these.
     @Override
     public NavigationBarView getNavigationBarView() {
@@ -3565,12 +3568,6 @@
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
 
-    //TODO(b/257041702) delete
-    @Override
-    public void makeExpandedVisible(boolean force) {
-        mShadeController.makeExpandedVisible(force);
-    }
-
     final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
         @Override
         public void onScreenTurningOn(Runnable onDrawn) {
@@ -4078,7 +4075,9 @@
             return;
         }
 
-        mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+        if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+            mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+        }
     }
 
     @Override
@@ -4259,6 +4258,7 @@
                 @Override
                 public void onDozeAmountChanged(float linear, float eased) {
                     if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
+                            && !mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
                             && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
                         mLightRevealScrim.setRevealAmount(1f - linear);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 26e6db6..4beb87d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -15,23 +15,21 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -43,9 +41,9 @@
     private final List<Callback> mCallbacks = new ArrayList<>();
 
     private final Context mContext;
+    private final Executor mMainExecutor;
     private final UserManager mUserManager;
     private final UserTracker mUserTracker;
-    private final BroadcastDispatcher mBroadcastDispatcher;
     private final LinkedList<UserInfo> mProfiles;
     private boolean mListening;
     private int mCurrentUser;
@@ -53,12 +51,12 @@
     /**
      */
     @Inject
-    public ManagedProfileControllerImpl(Context context, UserTracker userTracker,
-            BroadcastDispatcher broadcastDispatcher) {
+    public ManagedProfileControllerImpl(Context context, @Main Executor mainExecutor,
+            UserTracker userTracker) {
         mContext = context;
+        mMainExecutor = mainExecutor;
         mUserManager = UserManager.get(mContext);
         mUserTracker = userTracker;
-        mBroadcastDispatcher = broadcastDispatcher;
         mProfiles = new LinkedList<UserInfo>();
     }
 
@@ -130,30 +128,34 @@
     }
 
     private void setListening(boolean listening) {
+        if (mListening == listening) {
+            return;
+        }
         mListening = listening;
         if (listening) {
             reloadManagedProfiles();
-
-            final IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
-            mBroadcastDispatcher.registerReceiver(
-                    mReceiver, filter, null /* handler */, UserHandle.ALL);
+            mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
         } else {
-            mBroadcastDispatcher.unregisterReceiver(mReceiver);
+            mUserTracker.removeCallback(mUserChangedCallback);
         }
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            reloadManagedProfiles();
-            for (Callback callback : mCallbacks) {
-                callback.onManagedProfileChanged();
-            }
-        }
-    };
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    reloadManagedProfiles();
+                    for (Callback callback : mCallbacks) {
+                        callback.onManagedProfileChanged();
+                    }
+                }
+
+                @Override
+                public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
+                    reloadManagedProfiles();
+                    for (Callback callback : mCallbacks) {
+                        callback.onManagedProfileChanged();
+                    }
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fb0d3e4..d500f99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -352,6 +352,11 @@
                 .getBoolean(R.bool.notification_scrim_transparent);
         updateScrims();
         mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+
+        // prepare() sets proper initial values for most states
+        for (ScrimState state : ScrimState.values()) {
+            state.prepare(state);
+        }
     }
 
     /**
@@ -641,10 +646,6 @@
     private void setTransitionToFullShade(boolean transitioning) {
         if (transitioning != mTransitioningToFullShade) {
             mTransitioningToFullShade = transitioning;
-            if (transitioning) {
-                // Let's make sure the shade locked is ready
-                ScrimState.SHADE_LOCKED.prepare(mState);
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 52430d3..0e9d3ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -146,18 +146,12 @@
             mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
             mNotifAlpha = 1f;
             mFrontAlpha = 0f;
-            mBehindTint = Color.BLACK;
+            mBehindTint = mClipQsScrim ? Color.TRANSPARENT : Color.BLACK;
 
             if (mClipQsScrim) {
                 updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
             }
         }
-
-        // to make sure correct color is returned before "prepare" is called
-        @Override
-        public int getBehindTint() {
-            return Color.BLACK;
-        }
     },
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 946d7e4..4d914fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -52,6 +52,6 @@
      * Returns true if we should apply some coloring to the wifi icon that was rendered with the new
      * pipeline to help with debugging.
      */
-    // For now, just always apply the debug coloring if we've enabled the new icon.
-    fun useWifiDebugColoring(): Boolean = useNewWifiIcon()
+    fun useWifiDebugColoring(): Boolean =
+        featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
index 7aa5ee1..8ff9198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
@@ -23,9 +23,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.qs.SettingObserver
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.dagger.AirplaneTableLog
 import com.android.systemui.util.settings.GlobalSettings
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -58,7 +59,7 @@
 constructor(
     @Background private val bgHandler: Handler,
     private val globalSettings: GlobalSettings,
-    logger: ConnectivityPipelineLogger,
+    @AirplaneTableLog logger: TableLogBuffer,
     @Application scope: CoroutineScope,
 ) : AirplaneModeRepository {
     // TODO(b/254848912): Replace this with a generic SettingObserver coroutine once we have it.
@@ -82,7 +83,12 @@
                 awaitClose { observer.isListening = false }
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "isAirplaneMode")
+            .logDiffsForTable(
+                logger,
+                columnPrefix = "",
+                columnName = "isAirplaneMode",
+                initialValue = false
+            )
             .stateIn(
                 scope,
                 started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index fe30c01..4a5342e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -36,16 +36,20 @@
  * [com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository] for
  * more details.
  */
+interface AirplaneModeViewModel {
+    /** True if the airplane mode icon is currently visible in the status bar. */
+    val isAirplaneModeIconVisible: StateFlow<Boolean>
+}
+
 @SysUISingleton
-class AirplaneModeViewModel
+class AirplaneModeViewModelImpl
 @Inject
 constructor(
     interactor: AirplaneModeInteractor,
     logger: ConnectivityPipelineLogger,
     @Application private val scope: CoroutineScope,
-) {
-    /** True if the airplane mode icon is currently visible in the status bar. */
-    val isAirplaneModeIconVisible: StateFlow<Boolean> =
+) : AirplaneModeViewModel {
+    override val isAirplaneModeIconVisible: StateFlow<Boolean> =
         combine(interactor.isAirplaneMode, interactor.isForceHidden) {
                 isAirplaneMode,
                 isAirplaneIconForceHidden ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt
new file mode 100644
index 0000000..4f70f66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Airplane mode logs in table format. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class AirplaneTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 0662fb3..fb67f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -21,6 +21,8 @@
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryImpl
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
@@ -33,6 +35,8 @@
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -43,12 +47,18 @@
     abstract fun airplaneModeRepository(impl: AirplaneModeRepositoryImpl): AirplaneModeRepository
 
     @Binds
+    abstract fun airplaneModeViewModel(impl: AirplaneModeViewModelImpl): AirplaneModeViewModel
+
+    @Binds
     abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
 
     @Binds
     abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
 
     @Binds
+    abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
+
+    @Binds
     abstract fun mobileConnectionsRepository(
         impl: MobileConnectionsRepositoryImpl
     ): MobileConnectionsRepository
@@ -71,5 +81,13 @@
         fun provideWifiTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
             return factory.create("WifiTableLog", 100)
         }
+
+        @JvmStatic
+        @Provides
+        @SysUISingleton
+        @AirplaneTableLog
+        fun provideAirplaneTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+            return factory.create("AirplaneTableLog", 30)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
index 8436b13..a682a57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
@@ -31,15 +31,19 @@
             if (prevVal is Inactive) {
                 return
             }
-            row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
 
             if (prevVal is CarrierMerged) {
                 // The only difference between CarrierMerged and Inactive is the type
+                row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
                 return
             }
 
             // When changing from Active to Inactive, we need to log diffs to all the fields.
-            logDiffsFromActiveToNotActive(prevVal as Active, row)
+            logFullNonActiveNetwork(TYPE_INACTIVE, row)
+        }
+
+        override fun logFull(row: TableRowLogger) {
+            logFullNonActiveNetwork(TYPE_INACTIVE, row)
         }
     }
 
@@ -56,15 +60,15 @@
             if (prevVal is CarrierMerged) {
                 return
             }
-            row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
 
             if (prevVal is Inactive) {
                 // The only difference between CarrierMerged and Inactive is the type.
+                row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
                 return
             }
 
             // When changing from Active to CarrierMerged, we need to log diffs to all the fields.
-            logDiffsFromActiveToNotActive(prevVal as Active, row)
+            logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row)
         }
     }
 
@@ -121,7 +125,11 @@
                 row.logChange(COL_VALIDATED, isValidated)
             }
             if (prevVal !is Active || prevVal.level != level) {
-                row.logChange(COL_LEVEL, level ?: LEVEL_DEFAULT)
+                if (level != null) {
+                    row.logChange(COL_LEVEL, level)
+                } else {
+                    row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+                }
             }
             if (prevVal !is Active || prevVal.ssid != ssid) {
                 row.logChange(COL_SSID, ssid)
@@ -143,7 +151,6 @@
             }
         }
 
-
         override fun toString(): String {
             // Only include the passpoint-related values in the string if we have them. (Most
             // networks won't have them so they'll be mostly clutter.)
@@ -170,21 +177,15 @@
         }
     }
 
-    internal fun logDiffsFromActiveToNotActive(prevActive: Active, row: TableRowLogger) {
+    internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) {
+        row.logChange(COL_NETWORK_TYPE, type)
         row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
         row.logChange(COL_VALIDATED, false)
         row.logChange(COL_LEVEL, LEVEL_DEFAULT)
         row.logChange(COL_SSID, null)
-
-        if (prevActive.isPasspointAccessPoint) {
-            row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
-        }
-        if (prevActive.isOnlineSignUpForPasspointAccessPoint) {
-            row.logChange(COL_ONLINE_SIGN_UP, false)
-        }
-        if (prevActive.passpointProviderFriendlyName != null) {
-            row.logChange(COL_PASSPOINT_NAME, null)
-        }
+        row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
+        row.logChange(COL_ONLINE_SIGN_UP, false)
+        row.logChange(COL_PASSPOINT_NAME, null)
     }
 }
 
@@ -201,5 +202,5 @@
 const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
 const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
 
-const val LEVEL_DEFAULT = -1
-const val NETWORK_ID_DEFAULT = -1
+val LEVEL_DEFAULT: String? = null
+val NETWORK_ID_DEFAULT: String? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 3a3e611..ec935fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -34,16 +34,36 @@
  * This interactor processes information from our data layer into information that the UI layer can
  * use.
  */
-@SysUISingleton
-class WifiInteractor @Inject constructor(
-    connectivityRepository: ConnectivityRepository,
-    wifiRepository: WifiRepository,
-) {
+interface WifiInteractor {
     /**
      * The SSID (service set identifier) of the wifi network. Null if we don't have a network, or
      * have a network but no valid SSID.
      */
-    val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
+    val ssid: Flow<String?>
+
+    /** Our current enabled status. */
+    val isEnabled: Flow<Boolean>
+
+    /** Our current default status. */
+    val isDefault: Flow<Boolean>
+
+    /** Our current wifi network. See [WifiNetworkModel]. */
+    val wifiNetwork: Flow<WifiNetworkModel>
+
+    /** Our current wifi activity. See [WifiActivityModel]. */
+    val activity: StateFlow<WifiActivityModel>
+
+    /** True if we're configured to force-hide the wifi icon and false otherwise. */
+    val isForceHidden: Flow<Boolean>
+}
+
+@SysUISingleton
+class WifiInteractorImpl @Inject constructor(
+    connectivityRepository: ConnectivityRepository,
+    wifiRepository: WifiRepository,
+) : WifiInteractor {
+
+    override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
         when (info) {
             is WifiNetworkModel.Inactive -> null
             is WifiNetworkModel.CarrierMerged -> null
@@ -56,20 +76,15 @@
         }
     }
 
-    /** Our current enabled status. */
-    val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
+    override val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
 
-    /** Our current default status. */
-    val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
+    override val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
 
-    /** Our current wifi network. See [WifiNetworkModel]. */
-    val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
+    override val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
 
-    /** Our current wifi activity. See [WifiActivityModel]. */
-    val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
+    override val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
 
-    /** True if we're configured to force-hide the wifi icon and false otherwise. */
-    val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
+    override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
         it.contains(ConnectivitySlot.WIFI)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index d84cbcc..6875b52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -120,6 +120,7 @@
                 @Override
                 public void onUserChanged(int newUser, @NonNull Context userContext) {
                     mCurrentUserId = newUser;
+                    updateClock();
                 }
             };
 
@@ -190,7 +191,6 @@
             filter.addAction(Intent.ACTION_TIME_CHANGED);
             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
             filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
 
             // NOTE: This receiver could run before this method returns, as it's not dispatching
             // on the main thread and BroadcastDispatcher may not need to register with Context.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index b234e9c..63b9ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -28,11 +28,14 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.UserTracker;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -45,22 +48,34 @@
 
     private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
 
+    private final UserTracker mUserTracker;
     private AlarmManager mAlarmManager;
     private AlarmManager.AlarmClockInfo mNextAlarm;
 
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    updateNextAlarm();
+                }
+            };
+
     /**
      */
     @Inject
     public NextAlarmControllerImpl(
+            @Main Executor mainExecutor,
             AlarmManager alarmManager,
             BroadcastDispatcher broadcastDispatcher,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            UserTracker userTracker) {
         dumpManager.registerDumpable("NextAlarmController", this);
         mAlarmManager = alarmManager;
+        mUserTracker = userTracker;
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
         broadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
+        mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
         updateNextAlarm();
     }
 
@@ -98,14 +113,13 @@
 
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
-        if (action.equals(Intent.ACTION_USER_SWITCHED)
-                || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+        if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
             updateNextAlarm();
         }
     }
 
     private void updateNextAlarm() {
-        mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+        mNextAlarm = mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
         fireNextAlarmChanged();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 0c72b78..2b29885 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.user;
 
 import android.app.Activity;
+import android.os.UserHandle;
 
 import com.android.settingslib.users.EditUserInfoController;
 import com.android.systemui.user.data.repository.UserRepositoryModule;
@@ -51,4 +52,22 @@
     @IntoMap
     @ClassKey(UserSwitcherActivity.class)
     public abstract Activity provideUserSwitcherActivity(UserSwitcherActivity activity);
+
+    /**
+     * Provides the {@link UserHandle} for the user associated with this System UI process.
+     *
+     * <p>Note that this is static and unchanging for the life-time of the process we are running
+     * in. It can be <i>different</i> from the user that is the currently-selected user, which may
+     * be associated with a different System UI process.
+     *
+     * <p>For example, the System UI process which creates all the windows and renders UI is always
+     * the one associated with the primary user on the device. However, if the user is switched to
+     * another, non-primary user (for example user "X"), then a secondary System UI process will be
+     * spawned. While the original primary user process continues to be the only one rendering UI,
+     * the new system UI process may be used for things like file or content access.
+     */
+    @Provides
+    public static UserHandle provideUserHandle() {
+        return new UserHandle(UserHandle.myUserId());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 4c9b8e4..c0f0390 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -242,7 +242,15 @@
             val isUserSwitcherEnabled =
                 globalSettings.getIntForUser(
                     Settings.Global.USER_SWITCHER_ENABLED,
-                    0,
+                    if (
+                        appContext.resources.getBoolean(
+                            com.android.internal.R.bool.config_showUserSwitcherByDefault
+                        )
+                    ) {
+                        1
+                    } else {
+                        0
+                    },
                     UserHandle.USER_SYSTEM,
                 ) != 0
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index c5b697c..512fadf 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -114,9 +114,9 @@
 
     private val callbackMutex = Mutex()
     private val callbacks = mutableSetOf<UserCallback>()
-    private val userInfos =
-        combine(repository.userSwitcherSettings, repository.userInfos) { settings, userInfos ->
-            userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }.filter { it.isFull }
+    private val userInfos: Flow<List<UserInfo>> =
+        repository.userInfos.map { userInfos ->
+            userInfos.filter { it.isFull }
         }
 
     /** List of current on-device users to select from. */
@@ -493,7 +493,7 @@
 
     fun showUserSwitcher(context: Context, expandable: Expandable) {
         if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
-            showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog)
+            showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog(expandable))
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
index 85c2964..14cc3e7 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
@@ -18,11 +18,13 @@
 package com.android.systemui.user.domain.model
 
 import android.os.UserHandle
+import com.android.systemui.animation.Expandable
 import com.android.systemui.qs.user.UserSwitchDialogController
 
 /** Encapsulates a request to show a dialog. */
 sealed class ShowDialogRequestModel(
     open val dialogShower: UserSwitchDialogController.DialogShower? = null,
+    open val expandable: Expandable? = null,
 ) {
     data class ShowAddUserDialog(
         val userHandle: UserHandle,
@@ -45,5 +47,7 @@
     ) : ShowDialogRequestModel(dialogShower)
 
     /** Show the user switcher dialog */
-    object ShowUserSwitcherDialog : ShowDialogRequestModel()
+    data class ShowUserSwitcherDialog(
+        override val expandable: Expandable?,
+    ) : ShowDialogRequestModel()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt
new file mode 100644
index 0000000..3fe2a7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.user.ui.dialog
+
+import android.app.Dialog
+import android.content.DialogInterface
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
+
+/** Extracted from [UserSwitchDialogController] */
+class DialogShowerImpl(
+    private val animateFrom: Dialog,
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+) : DialogInterface by animateFrom, DialogShower {
+    override fun showDialog(dialog: Dialog, cuj: DialogCuj) {
+        dialogLaunchAnimator.showFromDialog(dialog, animateFrom = animateFrom, cuj)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
index ed25898..b8ae257 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
@@ -60,6 +60,7 @@
         setView(gridFrame)
 
         adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
+        adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 4141054..d451230 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -133,7 +133,10 @@
                     }
                 currentDialog = dialog
 
-                if (request.dialogShower != null && dialogCuj != null) {
+                val controller = request.expandable?.dialogLaunchController(dialogCuj)
+                if (controller != null) {
+                    dialogLaunchAnimator.get().show(dialog, controller)
+                } else if (request.dialogShower != null && dialogCuj != null) {
                     request.dialogShower?.showDialog(dialog, dialogCuj)
                 } else {
                     dialog.show()
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 0910ea3..37115ad 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -19,6 +19,8 @@
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.ui.drawable.CircularDrawable
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.user.domain.interactor.GuestUserInteractor
@@ -144,7 +146,12 @@
     ): UserViewModel {
         return UserViewModel(
             viewKey = model.id,
-            name = model.name,
+            name =
+                if (model.isGuest && model.isSelected) {
+                    Text.Resource(R.string.guest_exit_quick_settings_button)
+                } else {
+                    model.name
+                },
             image = CircularDrawable(model.image),
             isSelectionMarkerVisible = model.isSelected,
             alpha =
diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
index 5b16ae9..b311318 100644
--- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
@@ -22,11 +22,22 @@
  * Run a block within a [Trace] section.
  * Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
  */
-inline fun <T> traceSection(tag: String, block: () -> T): T {
-    Trace.beginSection(tag)
-    try {
-        return block()
-    } finally {
-        Trace.endSection()
+inline fun <T> traceSection(tag: String, block: () -> T): T =
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+            Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
+            try {
+                block()
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_APP)
+            }
+        } else {
+            block()
+        }
+
+class TraceUtils {
+    companion object {
+        inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable {
+            return Runnable { traceSection(tag) { block() } }
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index f71d596..b61b2e6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -20,7 +20,6 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.onStart
@@ -58,6 +57,22 @@
     onStart { emit(initialValue) }.pairwiseBy(transform)
 
 /**
+ * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
+ *
+ *
+ * The output of [getInitialValue] will be used as the "old" value for the first emission. As
+ * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before
+ * returning the initial value.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T, R> Flow<T>.pairwiseBy(
+    getInitialValue: suspend () -> T,
+    transform: suspend (previousValue: T, newValue: T) -> R,
+): Flow<R> =
+    onStart { emit(getInitialValue()) }.pairwiseBy(transform)
+
+/**
  * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new
  * Flow will not start emitting until it has received two emissions from the upstream Flow.
  *
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index afd582a..fa8c8982 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -87,17 +87,17 @@
     biometricSettingEnabledForUser = false,
     bouncerFullyShown = false,
     faceAndFpNotAuthenticated = false,
+    faceAuthAllowed = true,
     faceDisabled = false,
     faceLockedOut = false,
-    fpLockedOut = false,
     goingToSleep = false,
     keyguardAwake = false,
     keyguardGoingAway = false,
     listeningForFaceAssistant = false,
     occludingAppRequestingFaceAuth = false,
     primaryUser = false,
-    scanningAllowedByStrongAuth = false,
     secureCameraLaunched = false,
+    supportsDetect = true,
     switchingUser = false,
     udfpsBouncerShowing = false,
     udfpsFingerDown = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index e39b9b5..84f6d91 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -558,11 +558,40 @@
         configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged();
 
         verify(mView).onDensityOrFontScaleChanged();
-        verify(mKeyguardSecurityViewFlipperController).onDensityOrFontScaleChanged();
+        verify(mKeyguardSecurityViewFlipperController).clearViews();
         verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
                 any(KeyguardSecurityCallback.class));
     }
 
+    @Test
+    public void onThemeChanged() {
+        ArgumentCaptor<ConfigurationController.ConfigurationListener>
+                configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+                ConfigurationController.ConfigurationListener.class);
+        mKeyguardSecurityContainerController.onViewAttached();
+        verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+        configurationListenerArgumentCaptor.getValue().onThemeChanged();
+
+        verify(mView).reloadColors();
+        verify(mKeyguardSecurityViewFlipperController).clearViews();
+        verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+                any(KeyguardSecurityCallback.class));
+    }
+
+    @Test
+    public void onUiModeChanged() {
+        ArgumentCaptor<ConfigurationController.ConfigurationListener>
+                configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+                ConfigurationController.ConfigurationListener.class);
+        mKeyguardSecurityContainerController.onViewAttached();
+        verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+        configurationListenerArgumentCaptor.getValue().onUiModeChanged();
+
+        verify(mView).reloadColors();
+        verify(mKeyguardSecurityViewFlipperController).clearViews();
+        verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+                any(KeyguardSecurityCallback.class));
+    }
 
     private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() {
         mKeyguardSecurityContainerController.onViewAttached();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index fd02ac9..1614b57 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -109,7 +109,7 @@
 
     @Test
     public void onDensityOrFontScaleChanged() {
-        mKeyguardSecurityViewFlipperController.onDensityOrFontScaleChanged();
+        mKeyguardSecurityViewFlipperController.clearViews();
         verify(mView).removeAllViews();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 63e1603..f0d651a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -27,6 +27,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
@@ -101,6 +102,8 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.annotation.NonNull;
+
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.InstanceId;
@@ -267,21 +270,9 @@
 
         // IBiometricsFace@1.0 does not support detection, only authentication.
         when(mFaceSensorProperties.isEmpty()).thenReturn(false);
+        when(mFaceSensorProperties.get(anyInt())).thenReturn(
+                createFaceSensorProperties(/* supportsFaceDetection = */ false));
 
-        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
-        componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
-                "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                "00000001" /* serialNumber */, "" /* softwareVersion */));
-        componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
-                "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                "vendor/version/revision" /* softwareVersion */));
-
-        when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal(
-                0 /* id */,
-                FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */,
-                componentInfo, FaceSensorProperties.TYPE_UNKNOWN,
-                false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
-                false /* resetLockoutRequiresChallenge */));
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
         when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
         when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
@@ -354,6 +345,28 @@
         when(mAuthController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(true);
     }
 
+    @NonNull
+    private FaceSensorPropertiesInternal createFaceSensorProperties(boolean supportsFaceDetection) {
+        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+        componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+                "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+                "00000001" /* serialNumber */, "" /* softwareVersion */));
+        componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+                "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+                "vendor/version/revision" /* softwareVersion */));
+
+
+        return new FaceSensorPropertiesInternal(
+                0 /* id */,
+                FaceSensorProperties.STRENGTH_STRONG,
+                1 /* maxTemplatesAllowed */,
+                componentInfo,
+                FaceSensorProperties.TYPE_UNKNOWN,
+                supportsFaceDetection /* supportsFaceDetection */,
+                true /* supportsSelfIllumination */,
+                false /* resetLockoutRequiresChallenge */);
+    }
+
     @After
     public void tearDown() {
         if (mMockitoSession != null) {
@@ -609,6 +622,64 @@
     }
 
     @Test
+    public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
+        // GIVEN unlocking with biometric is allowed
+        strongAuthNotRequired();
+
+        // THEN unlocking with face and fp is allowed
+        Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FACE));
+        Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FINGERPRINT));
+    }
+
+    @Test
+    public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
+        // GIVEN unlocking with biometric is not allowed
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+
+        // THEN unlocking with face is not allowed
+        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FACE));
+    }
+
+    @Test
+    public void testUnlockingWithFaceAllowed_fingerprintLockout() {
+        // GIVEN unlocking with biometric is allowed
+        strongAuthNotRequired();
+
+        // WHEN fingerprint is locked out
+        fingerprintErrorLockedOut();
+
+        // THEN unlocking with face is not allowed
+        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FACE));
+    }
+
+    @Test
+    public void testUnlockingWithFpAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
+        // GIVEN unlocking with biometric is not allowed
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+
+        // THEN unlocking with fingerprint is not allowed
+        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FINGERPRINT));
+    }
+
+    @Test
+    public void testUnlockingWithFpAllowed_fingerprintLockout() {
+        // GIVEN unlocking with biometric is allowed
+        strongAuthNotRequired();
+
+        // WHEN fingerprint is locked out
+        fingerprintErrorLockedOut();
+
+        // THEN unlocking with fingeprint is not allowed
+        Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FINGERPRINT));
+    }
+
+    @Test
     public void testTriesToAuthenticate_whenBouncer() {
         setKeyguardBouncerVisibility(true);
 
@@ -652,10 +723,9 @@
     }
 
     @Test
-    public void skipsAuthentication_whenEncryptedKeyguard() {
-        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
-                STRONG_AUTH_REQUIRED_AFTER_BOOT);
-        mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+    public void skipsAuthentication_whenStrongAuthRequired_nonBypass() {
+        lockscreenBypassIsNotAllowed();
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
 
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
@@ -665,15 +735,48 @@
     }
 
     @Test
-    public void requiresAuthentication_whenEncryptedKeyguard_andBypass() {
-        testStrongAuthExceptOnBouncer(
-                STRONG_AUTH_REQUIRED_AFTER_BOOT);
+    public void faceDetect_whenStrongAuthRequiredAndBypass() {
+        // GIVEN bypass is enabled, face detection is supported and strong auth is required
+        lockscreenBypassIsAllowed();
+        supportsFaceDetection();
+        strongAuthRequiredEncrypted();
+        keyguardIsVisible();
+
+        // WHEN the device wakes up
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+
+        // FACE detect is triggered, not authenticate
+        verify(mFaceManager).detectFace(any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyBoolean());
+
+        // WHEN bouncer becomes visible
+        setKeyguardBouncerVisibility(true);
+        clearInvocations(mFaceManager);
+
+        // THEN face scanning is not run
+        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyBoolean());
+        verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
     }
 
     @Test
-    public void requiresAuthentication_whenTimeoutKeyguard_andBypass() {
-        testStrongAuthExceptOnBouncer(
-                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+    public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
+        // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
+        lockscreenBypassIsAllowed();
+        strongAuthRequiredEncrypted();
+        keyguardIsVisible();
+
+        // WHEN the device wakes up
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+
+        // FACE detect and authenticate are NOT triggered
+        verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyBoolean());
     }
 
     @Test
@@ -706,24 +809,6 @@
         assertThat(didFaceAuthRun).isFalse();
     }
 
-    private void testStrongAuthExceptOnBouncer(int strongAuth) {
-        when(mKeyguardBypassController.canBypass()).thenReturn(true);
-        mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
-        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
-
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        keyguardIsVisible();
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
-
-        // Stop scanning when bouncer becomes visible
-        setKeyguardBouncerVisibility(true);
-        clearInvocations(mFaceManager);
-        mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
-                anyBoolean());
-    }
-
     @Test
     public void testTriesToAuthenticate_whenAssistant() {
         mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
@@ -734,10 +819,9 @@
 
     @Test
     public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
-        mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
-        when(mKeyguardBypassController.canBypass()).thenReturn(true);
+        lockscreenBypassIsAllowed();
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
                 KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
                 new ArrayList<>());
@@ -757,26 +841,17 @@
     }
 
     @Test
-    public void testIgnoresAuth_whenLockdown() {
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
-                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+    public void testNoFaceAuth_whenLockDown() {
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+        userDeviceLockDown();
 
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         keyguardIsVisible();
+        mTestableLooper.processAllMessages();
+
         verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
                 anyBoolean());
-    }
-
-    @Test
-    public void testTriesToAuthenticate_whenLockout() {
-        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        mTestableLooper.processAllMessages();
-        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
-                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT);
-
-        keyguardIsVisible();
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+        verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
     }
 
     @Test
@@ -919,8 +994,6 @@
         verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
         verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
                 anyInt());
-//        resetFaceManager();
-//        resetFingerprintManager();
 
         when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
                 .thenReturn(fingerprintLockoutMode);
@@ -1263,7 +1336,7 @@
         mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
         setKeyguardBouncerVisibility(false /* isVisible */);
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
-        when(mKeyguardBypassController.canBypass()).thenReturn(true);
+        lockscreenBypassIsAllowed();
         keyguardIsVisible();
 
         // WHEN status bar state reports a change to the keyguard that would normally indicate to
@@ -1284,6 +1357,24 @@
     }
 
     @Test
+    public void testRequestFaceAuthFromOccludingApp_whenInvoked_startsFaceAuth() {
+        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+
+        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
+    }
+
+    @Test
+    public void testRequestFaceAuthFromOccludingApp_whenInvoked_stopsFaceAuth() {
+        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+
+        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
+
+        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+
+        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
+    }
+
+    @Test
     public void testRequireUnlockForNfc_Broadcast() {
         KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
         mKeyguardUpdateMonitor.registerCallback(callback);
@@ -1429,11 +1520,9 @@
         userNotCurrentlySwitching();
 
         // This disables face auth
-        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
-                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
         mTestableLooper.processAllMessages();
 
-
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
     }
 
@@ -1897,6 +1986,109 @@
         );
     }
 
+    @Test
+    public void testStrongAuthChange_lockDown_stopsFpAndFaceListeningState() {
+        // GIVEN device is listening for face and fingerprint
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+        keyguardIsVisible();
+
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                anyInt());
+
+        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+        final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
+        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+        mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel;
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN strong auth changes and device is in user lockdown
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+        userDeviceLockDown();
+        mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(getCurrentUser());
+        mTestableLooper.processAllMessages();
+
+        // THEN face and fingerprint listening are cancelled
+        verify(faceCancel).cancel();
+        verify(callback).onBiometricRunningStateChanged(
+                eq(false), eq(BiometricSourceType.FACE));
+        verify(fpCancel).cancel();
+        verify(callback).onBiometricRunningStateChanged(
+                eq(false), eq(BiometricSourceType.FINGERPRINT));
+    }
+
+    @Test
+    public void testNonStrongBiometricAllowedChanged_stopsFaceListeningState() {
+        // GIVEN device is listening for face and fingerprint
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+        keyguardIsVisible();
+
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+
+        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN non-strong biometric allowed changes
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+        mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(getCurrentUser());
+        mTestableLooper.processAllMessages();
+
+        // THEN face and fingerprint listening are cancelled
+        verify(faceCancel).cancel();
+        verify(callback).onBiometricRunningStateChanged(
+                eq(false), eq(BiometricSourceType.FACE));
+    }
+
+    @Test
+    public void testShouldListenForFace_withLockedDown_returnsFalse()
+            throws RemoteException {
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        supportsFaceDetection();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        userDeviceLockDown();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    private void userDeviceLockDown() {
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+        when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+    }
+
+    private void supportsFaceDetection() {
+        when(mFaceSensorProperties.get(anyInt()))
+                .thenReturn(createFaceSensorProperties(
+                        /* supportsFaceDetection = */ true));
+    }
+
+    private void lockscreenBypassIsAllowed() {
+        mockCanBypassLockscreen(true);
+    }
+
+    private void mockCanBypassLockscreen(boolean canBypass) {
+        mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+        when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
+    }
+
+    private void lockscreenBypassIsNotAllowed() {
+        mockCanBypassLockscreen(false);
+    }
+
     private void cleanupKeyguardUpdateMonitor() {
         if (mKeyguardUpdateMonitor != null) {
             mKeyguardUpdateMonitor.removeCallback(mTestCallback);
@@ -1990,9 +2182,16 @@
         );
     }
 
+    private void strongAuthRequiredEncrypted() {
+        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+    }
+
     private void strongAuthNotRequired() {
         when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
                 .thenReturn(0);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
     }
 
     private void currentUserDoesNotHaveTrust() {
@@ -2033,6 +2232,10 @@
         setKeyguardBouncerVisibility(true);
     }
 
+    private void bouncerNotVisible() {
+        setKeyguardBouncerVisibility(false);
+    }
+
     private void setKeyguardBouncerVisibility(boolean isVisible) {
         mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 181839a..0627fc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -77,7 +77,6 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.decor.CornerDecorProvider;
 import com.android.systemui.decor.CutoutDecorProviderFactory;
 import com.android.systemui.decor.CutoutDecorProviderImpl;
@@ -132,8 +131,6 @@
     @Mock
     private TunerService mTunerService;
     @Mock
-    private BroadcastDispatcher mBroadcastDispatcher;
-    @Mock
     private UserTracker mUserTracker;
     @Mock
     private PrivacyDotViewController mDotViewController;
@@ -223,8 +220,8 @@
                 mExecutor));
 
         mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
-                mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
-                mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
+                mTunerService, mUserTracker, mDotViewController, mThreadFactory,
+                mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
             @Override
             public void start() {
                 super.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 57ca9c0..9d39a8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -34,7 +34,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -88,6 +87,8 @@
 import com.android.systemui.util.leak.ReferenceTestUtils;
 import com.android.systemui.utils.os.FakeHandler;
 
+import com.google.common.util.concurrent.AtomicDouble;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -710,7 +711,7 @@
     }
 
     @Test
-    public void onSingleTap_enabled_scaleIsChanged() {
+    public void onSingleTap_enabled_scaleAnimates() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
                     Float.NaN);
@@ -721,14 +722,28 @@
         });
 
         final View mirrorView = mWindowManager.getAttachedView();
+
         final long timeout = SystemClock.uptimeMillis() + 1000;
-        while (SystemClock.uptimeMillis() < timeout) {
-            SystemClock.sleep(10);
-            if (Float.compare(1.0f, mirrorView.getScaleX()) < 0) {
-                return;
+        final AtomicDouble maxScaleX = new AtomicDouble();
+        final Runnable onAnimationFrame = new Runnable() {
+            @Override
+            public void run() {
+                // For some reason the fancy way doesn't compile...
+//                maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
+                final double oldMax = maxScaleX.get();
+                final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
+                assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+
+                if (SystemClock.uptimeMillis() < timeout) {
+                    mirrorView.postOnAnimation(this);
+                }
             }
-        }
-        fail("MirrorView scale is not changed");
+        };
+        mirrorView.postOnAnimation(onAnimationFrame);
+
+        waitForIdleSync();
+
+        ReferenceTestUtils.waitForCondition(() -> maxScaleX.get() > 1.0);
     }
 
     @Test
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 eb8c823..b765ab3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -26,6 +26,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.LightRevealScrim
@@ -77,6 +78,7 @@
     @Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var featureFlags: FeatureFlags
     @Mock private lateinit var lightRevealScrim: LightRevealScrim
     @Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
 
@@ -106,6 +108,7 @@
             biometricUnlockController,
             udfpsControllerProvider,
             statusBarStateController,
+            featureFlags,
             rippleView
         )
         controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index e7d5632..3c40835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -47,6 +47,7 @@
 import android.view.WindowManager
 import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
 import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
 import android.view.WindowMetrics
 import androidx.test.filters.SmallTest
 import com.airbnb.lottie.LottieAnimationView
@@ -423,6 +424,21 @@
     }
 
     @Test
+    fun testLayoutParams_isKeyguardDialogType() =
+        testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+            sideFpsController.overlayOffsets = sensorLocation
+            sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+            overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+            executor.runAllReady()
+
+            verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+
+            val lpType = overlayViewParamsCaptor.value.type
+
+            assertThat((lpType and TYPE_KEYGUARD_DIALOG) != 0).isTrue()
+        }
+
+    @Test
     fun testLayoutParams_hasNoMoveAnimationWindowFlag() =
         testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
             sideFpsController.overlayOffsets = sensorLocation
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index acdafe3..b267a5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -70,8 +70,13 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.biometrics.udfps.InteractionEvent;
+import com.android.systemui.biometrics.udfps.NormalizedTouchData;
+import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessorResult;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.plugins.FalsingManager;
@@ -190,6 +195,8 @@
     private AlternateUdfpsTouchProvider mAlternateTouchProvider;
     @Mock
     private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+    @Mock
+    private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
 
     // Capture listeners so that they can be used to send events
     @Captor
@@ -275,7 +282,7 @@
                 mDisplayManager, mHandler, mConfigurationController, mSystemClock,
                 mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
                 mActivityLaunchAnimator, alternateTouchProvider, mBiometricsExecutor,
-                mPrimaryBouncerInteractor);
+                mPrimaryBouncerInteractor, mSinglePointerTouchProcessor);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -1086,4 +1093,100 @@
                 anyString(),
                 any());
     }
+
+    @Test
+    public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath()
+            throws RemoteException {
+        // Disable new touch detection.
+        when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(false);
+
+        // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+        initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+        // Configure UdfpsView to accept the ACTION_DOWN event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+        // WHEN ACTION_DOWN is received
+        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+        mBiometricsExecutor.runAllReady();
+        downEvent.recycle();
+
+        // AND ACTION_MOVE is received
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        mBiometricsExecutor.runAllReady();
+        moveEvent.recycle();
+
+        // AND ACTION_UP is received
+        MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+        mBiometricsExecutor.runAllReady();
+        upEvent.recycle();
+
+        // THEN the old FingerprintManager path is invoked.
+        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
+                anyFloat(), anyFloat());
+        verify(mFingerprintManager).onPointerUp(anyLong(), anyInt());
+    }
+
+    @Test
+    public void onTouch_withNewTouchDetection_shouldCallOldFingerprintManagerPath()
+            throws RemoteException {
+        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+                0L);
+        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+                InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+        final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
+                InteractionEvent.UP, 1 /* pointerId */, touchData);
+
+        // Enable new touch detection.
+        when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+        // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+        initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+        // Configure UdfpsView to accept the ACTION_DOWN event
+        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+        // WHEN ACTION_DOWN is received
+        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+                processorResultDown);
+        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+        mBiometricsExecutor.runAllReady();
+        downEvent.recycle();
+
+        // AND ACTION_UP is received
+        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+                processorResultUp);
+        MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+        mBiometricsExecutor.runAllReady();
+        upEvent.recycle();
+
+        // THEN the new FingerprintManager path is invoked.
+        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+        verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
+                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
new file mode 100644
index 0000000..4f89b69
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
+    val underTest = BoundingBoxOverlapDetector()
+
+    @Test
+    fun isGoodOverlap() {
+        val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
+        val actual = underTest.isGoodOverlap(touchData, SENSOR)
+
+        assertThat(actual).isEqualTo(testCase.expected)
+    }
+
+    data class TestCase(val x: Int, val y: Int, val expected: Boolean)
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun data(): List<TestCase> =
+            listOf(
+                    genPositiveTestCases(
+                        validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+                        validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+                    ),
+                    genNegativeTestCases(
+                        invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+                        invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+                        validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+                        validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+                    )
+                )
+                .flatten()
+    }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+    NormalizedTouchData(
+        POINTER_ID,
+        x = 0f,
+        y = 0f,
+        NATIVE_MINOR,
+        NATIVE_MAJOR,
+        ORIENTATION,
+        TIME,
+        GESTURE_START
+    )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+
+private fun genTestCases(
+    xs: List<Int>,
+    ys: List<Int>,
+    expected: Boolean
+): List<BoundingBoxOverlapDetectorTest.TestCase> {
+    return xs.flatMap { x ->
+        ys.map { y -> BoundingBoxOverlapDetectorTest.TestCase(x, y, expected) }
+    }
+}
+
+private fun genPositiveTestCases(
+    validXs: List<Int>,
+    validYs: List<Int>,
+) = genTestCases(validXs, validYs, expected = true)
+
+private fun genNegativeTestCases(
+    invalidXs: List<Int>,
+    invalidYs: List<Int>,
+    validXs: List<Int>,
+    validYs: List<Int>,
+): List<BoundingBoxOverlapDetectorTest.TestCase> {
+    return genTestCases(invalidXs, validYs, expected = false) +
+        genTestCases(validXs, invalidYs, expected = false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
new file mode 100644
index 0000000..834d0a6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
@@ -0,0 +1,90 @@
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
+
+    @Test
+    fun isWithinSensor() {
+        val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
+        val actual = touchData.isWithinSensor(SENSOR)
+
+        assertThat(actual).isEqualTo(testCase.expected)
+    }
+
+    data class TestCase(val x: Int, val y: Int, val expected: Boolean)
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun data(): List<TestCase> =
+            listOf(
+                    genPositiveTestCases(
+                        validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+                        validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+                    ),
+                    genNegativeTestCases(
+                        invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+                        invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+                        validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+                        validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+                    )
+                )
+                .flatten()
+    }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+    NormalizedTouchData(
+        POINTER_ID,
+        x = 0f,
+        y = 0f,
+        NATIVE_MINOR,
+        NATIVE_MAJOR,
+        ORIENTATION,
+        TIME,
+        GESTURE_START
+    )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+
+private fun genTestCases(
+    xs: List<Int>,
+    ys: List<Int>,
+    expected: Boolean
+): List<NormalizedTouchDataTest.TestCase> {
+    return xs.flatMap { x -> ys.map { y -> NormalizedTouchDataTest.TestCase(x, y, expected) } }
+}
+
+private fun genPositiveTestCases(
+    validXs: List<Int>,
+    validYs: List<Int>,
+) = genTestCases(validXs, validYs, expected = true)
+
+private fun genNegativeTestCases(
+    invalidXs: List<Int>,
+    invalidYs: List<Int>,
+    validXs: List<Int>,
+    validYs: List<Int>,
+): List<NormalizedTouchDataTest.TestCase> {
+    return genTestCases(invalidXs, validYs, expected = false) +
+        genTestCases(validXs, invalidYs, expected = false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
new file mode 100644
index 0000000..95c53b4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import android.view.MotionEvent
+import android.view.MotionEvent.INVALID_POINTER_ID
+import android.view.MotionEvent.PointerProperties
+import android.view.Surface
+import android.view.Surface.Rotation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.UdfpsOverlayParams
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() {
+    private val overlapDetector = FakeOverlapDetector()
+    private val underTest = SinglePointerTouchProcessor(overlapDetector)
+
+    @Test
+    fun processTouch() {
+        overlapDetector.shouldReturn = testCase.isGoodOverlap
+
+        val actual =
+            underTest.processTouch(
+                testCase.event,
+                testCase.previousPointerOnSensorId,
+                testCase.overlayParams,
+            )
+
+        assertThat(actual).isInstanceOf(testCase.expected.javaClass)
+        if (actual is TouchProcessorResult.ProcessedTouch) {
+            assertThat(actual).isEqualTo(testCase.expected)
+        }
+    }
+
+    data class TestCase(
+        val event: MotionEvent,
+        val isGoodOverlap: Boolean,
+        val previousPointerOnSensorId: Int,
+        val overlayParams: UdfpsOverlayParams,
+        val expected: TouchProcessorResult,
+    ) {
+        override fun toString(): String {
+            val expectedOutput =
+                if (expected is TouchProcessorResult.ProcessedTouch) {
+                    expected.event.toString() +
+                        ", (x: ${expected.touchData.x}, y: ${expected.touchData.y})" +
+                        ", pointerOnSensorId: ${expected.pointerOnSensorId}" +
+                        ", ..."
+                } else {
+                    TouchProcessorResult.Failure().toString()
+                }
+            return "{" +
+                MotionEvent.actionToString(event.action) +
+                ", (x: ${event.x}, y: ${event.y})" +
+                ", scale: ${overlayParams.scaleFactor}" +
+                ", rotation: " +
+                Surface.rotationToString(overlayParams.rotation) +
+                ", previousPointerOnSensorId: $previousPointerOnSensorId" +
+                ", ...} expected: {$expectedOutput}"
+        }
+    }
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun data(): List<TestCase> =
+            listOf(
+                    // MotionEvent.ACTION_DOWN
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_DOWN,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.DOWN,
+                        expectedPointerOnSensorId = POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_DOWN,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.DOWN,
+                        expectedPointerOnSensorId = POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_DOWN,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_DOWN,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    // MotionEvent.ACTION_MOVE
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_MOVE,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.DOWN,
+                        expectedPointerOnSensorId = POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_MOVE,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_MOVE,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_MOVE,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    // MotionEvent.ACTION_UP
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_UP,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_UP,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_UP,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_UP,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    // MotionEvent.ACTION_CANCEL
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_CANCEL,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.CANCEL,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_CANCEL,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = true,
+                        expectedInteractionEvent = InteractionEvent.CANCEL,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_CANCEL,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.CANCEL,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_CANCEL,
+                        previousPointerOnSensorId = POINTER_ID,
+                        isGoodOverlap = false,
+                        expectedInteractionEvent = InteractionEvent.CANCEL,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                )
+                .flatten() +
+                listOf(
+                        // Unsupported MotionEvent actions.
+                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_DOWN),
+                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_UP),
+                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
+                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
+                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT),
+                    )
+                    .flatten()
+    }
+}
+
+/* Display dimensions in native resolution and natural orientation. */
+private const val ROTATION_0_NATIVE_DISPLAY_WIDTH = 400
+private const val ROTATION_0_NATIVE_DISPLAY_HEIGHT = 600
+
+/*
+ * ROTATION_0 map:
+ * _ _ _ _
+ * _ _ O _
+ * _ _ _ _
+ * _ S _ _
+ * _ S _ _
+ * _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_0_NATIVE_SENSOR_BOUNDS =
+    Rect(
+        100, /* left */
+        300, /* top */
+        200, /* right */
+        500, /* bottom */
+    )
+private val ROTATION_0_INPUTS =
+    OrientationBasedInputs(
+        rotation = Surface.ROTATION_0,
+        nativeXWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+        nativeYWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+        nativeXOutsideSensor = 250f,
+        nativeYOutsideSensor = 150f,
+    )
+
+/*
+ * ROTATION_90 map:
+ * _ _ _ _ _ _
+ * _ O _ _ _ _
+ * _ _ _ S S _
+ * _ _ _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_90_NATIVE_SENSOR_BOUNDS =
+    Rect(
+        300, /* left */
+        200, /* top */
+        500, /* right */
+        300, /* bottom */
+    )
+private val ROTATION_90_INPUTS =
+    OrientationBasedInputs(
+        rotation = Surface.ROTATION_90,
+        nativeXWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+        nativeYWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+        nativeXOutsideSensor = 150f,
+        nativeYOutsideSensor = 150f,
+    )
+
+/* ROTATION_180 is not supported. It's treated the same as ROTATION_0. */
+private val ROTATION_180_INPUTS =
+    ROTATION_0_INPUTS.copy(
+        rotation = Surface.ROTATION_180,
+    )
+
+/*
+ * ROTATION_270 map:
+ * _ _ _ _ _ _
+ * _ S S _ _ _
+ * _ _ _ _ O _
+ * _ _ _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_270_NATIVE_SENSOR_BOUNDS =
+    Rect(
+        100, /* left */
+        100, /* top */
+        300, /* right */
+        200, /* bottom */
+    )
+private val ROTATION_270_INPUTS =
+    OrientationBasedInputs(
+        rotation = Surface.ROTATION_270,
+        nativeXWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+        nativeYWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+        nativeXOutsideSensor = 450f,
+        nativeYOutsideSensor = 250f,
+    )
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [MotionEvent]. */
+private val MOTION_EVENT =
+    obtainMotionEvent(
+        action = 0,
+        pointerId = POINTER_ID,
+        x = 0f,
+        y = 0f,
+        minor = 0f,
+        major = 0f,
+        orientation = ORIENTATION,
+        time = TIME,
+        gestureStart = GESTURE_START,
+    )
+
+/* Template [NormalizedTouchData]. */
+private val NORMALIZED_TOUCH_DATA =
+    NormalizedTouchData(
+        POINTER_ID,
+        x = 0f,
+        y = 0f,
+        NATIVE_MINOR,
+        NATIVE_MAJOR,
+        ORIENTATION,
+        TIME,
+        GESTURE_START
+    )
+
+/*
+ * Contains test inputs that are tied to a particular device orientation.
+ *
+ * "native" means in native resolution (not scaled).
+ */
+private data class OrientationBasedInputs(
+    @Rotation val rotation: Int,
+    val nativeXWithinSensor: Float,
+    val nativeYWithinSensor: Float,
+    val nativeXOutsideSensor: Float,
+    val nativeYOutsideSensor: Float,
+) {
+
+    fun toOverlayParams(scaleFactor: Float): UdfpsOverlayParams =
+        UdfpsOverlayParams(
+            sensorBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
+            overlayBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
+            naturalDisplayHeight = (ROTATION_0_NATIVE_DISPLAY_HEIGHT * scaleFactor).toInt(),
+            naturalDisplayWidth = (ROTATION_0_NATIVE_DISPLAY_WIDTH * scaleFactor).toInt(),
+            scaleFactor = scaleFactor,
+            rotation = rotation
+        )
+
+    fun getNativeX(isWithinSensor: Boolean): Float {
+        return if (isWithinSensor) nativeXWithinSensor else nativeXOutsideSensor
+    }
+
+    fun getNativeY(isWithinSensor: Boolean): Float {
+        return if (isWithinSensor) nativeYWithinSensor else nativeYOutsideSensor
+    }
+}
+
+private fun genPositiveTestCases(
+    motionEventAction: Int,
+    previousPointerOnSensorId: Int,
+    isGoodOverlap: Boolean,
+    expectedInteractionEvent: InteractionEvent,
+    expectedPointerOnSensorId: Int
+): List<SinglePointerTouchProcessorTest.TestCase> {
+    val scaleFactors = listOf(0.75f, 1f, 1.5f)
+    val orientations =
+        listOf(
+            ROTATION_0_INPUTS,
+            ROTATION_90_INPUTS,
+            ROTATION_180_INPUTS,
+            ROTATION_270_INPUTS,
+        )
+    return scaleFactors.flatMap { scaleFactor ->
+        orientations.map { orientation ->
+            val overlayParams = orientation.toOverlayParams(scaleFactor)
+            val nativeX = orientation.getNativeX(isGoodOverlap)
+            val nativeY = orientation.getNativeY(isGoodOverlap)
+            val event =
+                MOTION_EVENT.copy(
+                    action = motionEventAction,
+                    x = nativeX * scaleFactor,
+                    y = nativeY * scaleFactor,
+                    minor = NATIVE_MINOR * scaleFactor,
+                    major = NATIVE_MAJOR * scaleFactor,
+                )
+            val expectedTouchData =
+                NORMALIZED_TOUCH_DATA.copy(
+                    x = ROTATION_0_INPUTS.getNativeX(isGoodOverlap),
+                    y = ROTATION_0_INPUTS.getNativeY(isGoodOverlap),
+                )
+            val expected =
+                TouchProcessorResult.ProcessedTouch(
+                    event = expectedInteractionEvent,
+                    pointerOnSensorId = expectedPointerOnSensorId,
+                    touchData = expectedTouchData,
+                )
+            SinglePointerTouchProcessorTest.TestCase(
+                event = event,
+                isGoodOverlap = isGoodOverlap,
+                previousPointerOnSensorId = previousPointerOnSensorId,
+                overlayParams = overlayParams,
+                expected = expected,
+            )
+        }
+    }
+}
+
+private fun genTestCasesForUnsupportedAction(
+    motionEventAction: Int
+): List<SinglePointerTouchProcessorTest.TestCase> {
+    val isGoodOverlap = true
+    val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID)
+    return previousPointerOnSensorIds.map { previousPointerOnSensorId ->
+        val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f)
+        val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap)
+        val nativeY = ROTATION_0_INPUTS.getNativeY(isGoodOverlap)
+        val event =
+            MOTION_EVENT.copy(
+                action = motionEventAction,
+                x = nativeX,
+                y = nativeY,
+                minor = NATIVE_MINOR,
+                major = NATIVE_MAJOR,
+            )
+        SinglePointerTouchProcessorTest.TestCase(
+            event = event,
+            isGoodOverlap = isGoodOverlap,
+            previousPointerOnSensorId = previousPointerOnSensorId,
+            overlayParams = overlayParams,
+            expected = TouchProcessorResult.Failure(),
+        )
+    }
+}
+
+private fun obtainMotionEvent(
+    action: Int,
+    pointerId: Int,
+    x: Float,
+    y: Float,
+    minor: Float,
+    major: Float,
+    orientation: Float,
+    time: Long,
+    gestureStart: Long,
+): MotionEvent {
+    val pp = PointerProperties()
+    pp.id = pointerId
+    val pc = MotionEvent.PointerCoords()
+    pc.x = x
+    pc.y = y
+    pc.touchMinor = minor
+    pc.touchMajor = major
+    pc.orientation = orientation
+    return MotionEvent.obtain(
+        gestureStart /* downTime */,
+        time /* eventTime */,
+        action /* action */,
+        1 /* pointerCount */,
+        arrayOf(pp) /* pointerProperties */,
+        arrayOf(pc) /* pointerCoords */,
+        0 /* metaState */,
+        0 /* buttonState */,
+        1f /* xPrecision */,
+        1f /* yPrecision */,
+        0 /* deviceId */,
+        0 /* edgeFlags */,
+        0 /* source */,
+        0 /* flags */
+    )
+}
+
+private fun MotionEvent.copy(
+    action: Int = this.action,
+    pointerId: Int = this.getPointerId(0),
+    x: Float = this.rawX,
+    y: Float = this.rawY,
+    minor: Float = this.touchMinor,
+    major: Float = this.touchMajor,
+    orientation: Float = this.orientation,
+    time: Long = this.eventTime,
+    gestureStart: Long = this.downTime,
+) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart)
+
+private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt
new file mode 100644
index 0000000..4b88b44
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import android.content.pm.UserInfo
+import android.provider.Settings
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class ControlsSettingsRepositoryImplTest : SysuiTestCase() {
+
+    companion object {
+        private const val LOCKSCREEN_SHOW = Settings.Secure.LOCKSCREEN_SHOW_CONTROLS
+        private const val LOCKSCREEN_ACTION = Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS
+
+        private fun createUser(id: Int): UserInfo {
+            return UserInfo(id, "user_$id", 0)
+        }
+
+        private val ALL_USERS = (0..1).map { it to createUser(it) }.toMap()
+    }
+
+    private lateinit var underTest: ControlsSettingsRepository
+
+    private lateinit var testScope: TestScope
+    private lateinit var secureSettings: FakeSettings
+    private lateinit var userRepository: FakeUserRepository
+
+    @Before
+    fun setUp() {
+        secureSettings = FakeSettings()
+        userRepository = FakeUserRepository()
+        userRepository.setUserInfos(ALL_USERS.values.toList())
+
+        val coroutineDispatcher = UnconfinedTestDispatcher()
+        testScope = TestScope(coroutineDispatcher)
+
+        underTest =
+            ControlsSettingsRepositoryImpl(
+                scope = testScope.backgroundScope,
+                backgroundDispatcher = coroutineDispatcher,
+                userRepository = userRepository,
+                secureSettings = secureSettings,
+            )
+    }
+
+    @Test
+    fun showInLockScreen() =
+        testScope.runTest {
+            setUser(0)
+            val values = mutableListOf<Boolean>()
+            val job =
+                launch(UnconfinedTestDispatcher()) {
+                    underTest.canShowControlsInLockscreen.toList(values)
+                }
+            assertThat(values.last()).isFalse()
+
+            secureSettings.putBool(LOCKSCREEN_SHOW, true)
+            assertThat(values.last()).isTrue()
+
+            secureSettings.putBool(LOCKSCREEN_SHOW, false)
+            assertThat(values.last()).isFalse()
+
+            secureSettings.putBoolForUser(LOCKSCREEN_SHOW, true, 1)
+            assertThat(values.last()).isFalse()
+
+            setUser(1)
+            assertThat(values.last()).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun showInLockScreen_changesInOtherUsersAreNotQueued() =
+        testScope.runTest {
+            setUser(0)
+
+            val values = mutableListOf<Boolean>()
+            val job =
+                launch(UnconfinedTestDispatcher()) {
+                    underTest.canShowControlsInLockscreen.toList(values)
+                }
+
+            secureSettings.putBoolForUser(LOCKSCREEN_SHOW, true, 1)
+            secureSettings.putBoolForUser(LOCKSCREEN_SHOW, false, 1)
+
+            setUser(1)
+            assertThat(values.last()).isFalse()
+            assertThat(values).containsNoneIn(listOf(true))
+
+            job.cancel()
+        }
+
+    @Test
+    fun actionInLockScreen() =
+        testScope.runTest {
+            setUser(0)
+            val values = mutableListOf<Boolean>()
+            val job =
+                launch(UnconfinedTestDispatcher()) {
+                    underTest.allowActionOnTrivialControlsInLockscreen.toList(values)
+                }
+            assertThat(values.last()).isFalse()
+
+            secureSettings.putBool(LOCKSCREEN_ACTION, true)
+            assertThat(values.last()).isTrue()
+
+            secureSettings.putBool(LOCKSCREEN_ACTION, false)
+            assertThat(values.last()).isFalse()
+
+            secureSettings.putBoolForUser(LOCKSCREEN_ACTION, true, 1)
+            assertThat(values.last()).isFalse()
+
+            setUser(1)
+            assertThat(values.last()).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun actionInLockScreen_changesInOtherUsersAreNotQueued() =
+        testScope.runTest {
+            setUser(0)
+
+            val values = mutableListOf<Boolean>()
+            val job =
+                launch(UnconfinedTestDispatcher()) {
+                    underTest.allowActionOnTrivialControlsInLockscreen.toList(values)
+                }
+
+            secureSettings.putBoolForUser(LOCKSCREEN_ACTION, true, 1)
+            secureSettings.putBoolForUser(LOCKSCREEN_ACTION, false, 1)
+
+            setUser(1)
+            assertThat(values.last()).isFalse()
+            assertThat(values).containsNoneIn(listOf(true))
+
+            job.cancel()
+        }
+
+    @Test
+    fun valueIsUpdatedWhenNotSubscribed() =
+        testScope.runTest {
+            setUser(0)
+            assertThat(underTest.canShowControlsInLockscreen.value).isFalse()
+
+            secureSettings.putBool(LOCKSCREEN_SHOW, true)
+
+            assertThat(underTest.canShowControlsInLockscreen.value).isTrue()
+        }
+
+    private suspend fun setUser(id: Int) {
+        secureSettings.userId = id
+        userRepository.setSelectedUserInfo(ALL_USERS[id]!!)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt
new file mode 100644
index 0000000..8a1bed2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeControlsSettingsRepository : ControlsSettingsRepository {
+    private val _canShowControlsInLockscreen = MutableStateFlow(false)
+    override val canShowControlsInLockscreen = _canShowControlsInLockscreen.asStateFlow()
+    private val _allowActionOnTrivialControlsInLockscreen = MutableStateFlow(false)
+    override val allowActionOnTrivialControlsInLockscreen =
+        _allowActionOnTrivialControlsInLockscreen.asStateFlow()
+
+    fun setCanShowControlsInLockscreen(value: Boolean) {
+        _canShowControlsInLockscreen.value = value
+    }
+
+    fun setAllowActionOnTrivialControlsInLockscreen(value: Boolean) {
+        _allowActionOnTrivialControlsInLockscreen.value = value
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 4ed5649c..1d00d6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -18,30 +18,24 @@
 
 import android.content.Context
 import android.content.SharedPreferences
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.FakeControlsSettingsRepository
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.settings.SecureSettings
 import com.android.wm.shell.TaskViewFactory
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Answers
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.`when`
@@ -79,8 +73,6 @@
     @Mock
     private lateinit var secureSettings: SecureSettings
     @Mock
-    private lateinit var mainHandler: Handler
-    @Mock
     private lateinit var userContextProvider: UserContextProvider
 
     companion object {
@@ -91,17 +83,15 @@
 
     private lateinit var coordinator: ControlActionCoordinatorImpl
     private lateinit var action: ControlActionCoordinatorImpl.Action
+    private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(secureSettings.getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
-                .thenReturn(Settings.Secure
-                        .getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
-        `when`(secureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
-                0, UserHandle.USER_CURRENT))
-                .thenReturn(1)
+        controlsSettingsRepository = FakeControlsSettingsRepository()
+        controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
+        controlsSettingsRepository.setCanShowControlsInLockscreen(true)
 
         coordinator = spy(ControlActionCoordinatorImpl(
                 mContext,
@@ -115,7 +105,7 @@
                 vibratorHelper,
                 secureSettings,
                 userContextProvider,
-                mainHandler
+                controlsSettingsRepository
         ))
 
         val userContext = mock(Context::class.java)
@@ -128,9 +118,6 @@
         `when`(pref.getInt(DeviceControlsControllerImpl.PREFS_SETTINGS_DIALOG_ATTEMPTS, 0))
                 .thenReturn(2)
 
-        verify(secureSettings).registerContentObserverForUser(any(Uri::class.java),
-                anyBoolean(), any(ContentObserver::class.java), anyInt())
-
         `when`(cvh.cws.ci.controlId).thenReturn(ID)
         `when`(cvh.cws.control?.isAuthRequired()).thenReturn(true)
         action = spy(coordinator.Action(ID, {}, false, true))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index c31fd82..1b34706 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.controls.controller
 
 import android.app.PendingIntent
-import android.content.BroadcastReceiver
 import android.content.ComponentName
 import android.content.Context
 import android.content.ContextWrapper
@@ -31,7 +30,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.management.ControlsListingController
@@ -85,10 +83,8 @@
     @Mock
     private lateinit var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper
     @Mock
-    private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock
     private lateinit var listingController: ControlsListingController
-    @Mock(stubOnly = true)
+    @Mock
     private lateinit var userTracker: UserTracker
     @Mock
     private lateinit var userFileManager: UserFileManager
@@ -104,7 +100,7 @@
             ArgumentCaptor<ControlsBindingController.LoadCallback>
 
     @Captor
-    private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+    private lateinit var userTrackerCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
     @Captor
     private lateinit var listingCallbackCaptor:
             ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -170,16 +166,15 @@
                 uiController,
                 bindingController,
                 listingController,
-                broadcastDispatcher,
                 userFileManager,
+                userTracker,
                 Optional.of(persistenceWrapper),
-                mock(DumpManager::class.java),
-                userTracker
+                mock(DumpManager::class.java)
         )
         controller.auxiliaryPersistenceWrapper = auxiliaryPersistenceWrapper
 
-        verify(broadcastDispatcher).registerReceiver(
-            capture(broadcastReceiverCaptor), any(), any(), eq(UserHandle.ALL), anyInt(), any()
+        verify(userTracker).addCallback(
+            capture(userTrackerCallbackCaptor), any()
         )
 
         verify(listingController).addCallback(capture(listingCallbackCaptor))
@@ -227,11 +222,10 @@
                 uiController,
                 bindingController,
                 listingController,
-                broadcastDispatcher,
                 userFileManager,
+                userTracker,
                 Optional.of(persistenceWrapper),
-                mock(DumpManager::class.java),
-                userTracker
+                mock(DumpManager::class.java)
         )
         assertEquals(listOf(TEST_STRUCTURE_INFO), controller_other.getFavorites())
     }
@@ -518,14 +512,8 @@
         delayableExecutor.runAllReady()
 
         reset(persistenceWrapper)
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
-            putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
-        }
-        val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
-        `when`(pendingResult.sendingUserId).thenReturn(otherUser)
-        broadcastReceiverCaptor.value.pendingResult = pendingResult
 
-        broadcastReceiverCaptor.value.onReceive(mContext, intent)
+        userTrackerCallbackCaptor.value.onUserChanged(otherUser, mContext)
 
         verify(persistenceWrapper).changeFileAndBackupManager(any(), any())
         verify(persistenceWrapper).readFavorites()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 77f451f..48fc46b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -17,19 +17,18 @@
 package com.android.systemui.controls.dagger
 
 import android.testing.AndroidTestingRunner
-import android.provider.Settings
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.FakeControlsSettingsRepository
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
 import dagger.Lazy
 import java.util.Optional
 import org.junit.Assert.assertEquals
@@ -63,13 +62,13 @@
     @Mock
     private lateinit var lockPatternUtils: LockPatternUtils
     @Mock
-    private lateinit var secureSettings: SecureSettings
-    @Mock
     private lateinit var optionalControlsTileResourceConfiguration:
             Optional<ControlsTileResourceConfiguration>
     @Mock
     private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration
 
+    private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
+
     companion object {
         fun <T> eq(value: T): T = Mockito.eq(value) ?: value
     }
@@ -78,6 +77,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        controlsSettingsRepository = FakeControlsSettingsRepository()
+
         `when`(userTracker.userHandle.identifier).thenReturn(0)
         `when`(optionalControlsTileResourceConfiguration.orElse(any()))
             .thenReturn(controlsTileResourceConfiguration)
@@ -125,8 +126,7 @@
         `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(STRONG_AUTH_NOT_REQUIRED)
         `when`(keyguardStateController.isUnlocked()).thenReturn(false)
-        `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
-            .thenReturn(0)
+        controlsSettingsRepository.setCanShowControlsInLockscreen(false)
         val component = setupComponent(true)
 
         assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility())
@@ -137,9 +137,7 @@
         `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(STRONG_AUTH_NOT_REQUIRED)
         `when`(keyguardStateController.isUnlocked()).thenReturn(false)
-        `when`(secureSettings.getIntForUser(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
-                anyInt(), anyInt()))
-            .thenReturn(1)
+        controlsSettingsRepository.setCanShowControlsInLockscreen(true)
         val component = setupComponent(true)
 
         assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility())
@@ -147,8 +145,7 @@
 
     @Test
     fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() {
-        `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
-            .thenReturn(0)
+        controlsSettingsRepository.setCanShowControlsInLockscreen(false)
         `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(STRONG_AUTH_NOT_REQUIRED)
         `when`(keyguardStateController.isUnlocked()).thenReturn(true)
@@ -187,7 +184,7 @@
             lockPatternUtils,
             keyguardStateController,
             userTracker,
-            secureSettings,
+            controlsSettingsRepository,
             optionalControlsTileResourceConfiguration
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 7a2ba95..06a944e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -361,8 +361,7 @@
             assertThat(lp.getMarginEnd()).isEqualTo(margin);
         });
 
-        // The third view should be at the top end corner. No margin should be applied if not
-        // specified.
+        // The third view should be at the top end corner. No margin should be applied.
         verifyChange(thirdViewInfo, true, lp -> {
             assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
             assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -442,65 +441,129 @@
     }
 
     /**
-     * Ensures the root complication applies margin if specified.
+     * Ensures layout sets correct max width constraint.
      */
     @Test
-    public void testRootComplicationSpecifiedMargin() {
-        final int defaultMargin = 5;
-        final int complicationMargin = 10;
+    public void testWidthConstraint() {
+        final int maxWidth = 20;
         final ComplicationLayoutEngine engine =
-                new ComplicationLayoutEngine(mLayout, defaultMargin, mTouchSession, 0, 0);
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
 
-        final ViewInfo firstViewInfo = new ViewInfo(
+        final ViewInfo viewStartDirection = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_START,
+                        0,
+                        5,
+                        maxWidth),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+        final ViewInfo viewEndDirection = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_START,
+                        ComplicationLayoutParams.DIRECTION_END,
+                        0,
+                        5,
+                        maxWidth),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        addComplication(engine, viewStartDirection);
+        addComplication(engine, viewEndDirection);
+
+        // Verify both horizontal direction views have max width set correctly, and max height is
+        // not set.
+        verifyChange(viewStartDirection, false, lp -> {
+            assertThat(lp.matchConstraintMaxWidth).isEqualTo(maxWidth);
+            assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+        });
+        verifyChange(viewEndDirection, false, lp -> {
+            assertThat(lp.matchConstraintMaxWidth).isEqualTo(maxWidth);
+            assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+        });
+    }
+
+    /**
+     * Ensures layout sets correct max height constraint.
+     */
+    @Test
+    public void testHeightConstraint() {
+        final int maxHeight = 20;
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+        final ViewInfo viewUpDirection = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_BOTTOM
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_UP,
+                        0,
+                        5,
+                        maxHeight),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+        final ViewInfo viewDownDirection = new ViewInfo(
                 new ComplicationLayoutParams(
                         100,
                         100,
                         ComplicationLayoutParams.POSITION_TOP
                                 | ComplicationLayoutParams.POSITION_END,
                         ComplicationLayoutParams.DIRECTION_DOWN,
-                        0),
+                        0,
+                        5,
+                        maxHeight),
                 Complication.CATEGORY_STANDARD,
                 mLayout);
 
-        addComplication(engine, firstViewInfo);
+        addComplication(engine, viewUpDirection);
+        addComplication(engine, viewDownDirection);
 
-        final ViewInfo secondViewInfo = new ViewInfo(
+        // Verify both vertical direction views have max height set correctly, and max width is
+        // not set.
+        verifyChange(viewUpDirection, false, lp -> {
+            assertThat(lp.matchConstraintMaxHeight).isEqualTo(maxHeight);
+            assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
+        });
+        verifyChange(viewDownDirection, false, lp -> {
+            assertThat(lp.matchConstraintMaxHeight).isEqualTo(maxHeight);
+            assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
+        });
+    }
+
+    /**
+     * Ensures layout does not set any constraint if not specified.
+     */
+    @Test
+    public void testConstraintNotSetWhenNotSpecified() {
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+        final ViewInfo view = new ViewInfo(
                 new ComplicationLayoutParams(
                         100,
                         100,
                         ComplicationLayoutParams.POSITION_TOP
                                 | ComplicationLayoutParams.POSITION_END,
-                        ComplicationLayoutParams.DIRECTION_START,
-                        0),
-                Complication.CATEGORY_SYSTEM,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0,
+                        5),
+                Complication.CATEGORY_STANDARD,
                 mLayout);
 
-        addComplication(engine, secondViewInfo);
+        addComplication(engine, view);
 
-        firstViewInfo.clearInvocations();
-        secondViewInfo.clearInvocations();
-
-        final ViewInfo thirdViewInfo = new ViewInfo(
-                new ComplicationLayoutParams(
-                        100,
-                        100,
-                        ComplicationLayoutParams.POSITION_TOP
-                                | ComplicationLayoutParams.POSITION_END,
-                        ComplicationLayoutParams.DIRECTION_START,
-                        1,
-                        complicationMargin),
-                Complication.CATEGORY_SYSTEM,
-                mLayout);
-
-        addComplication(engine, thirdViewInfo);
-
-        // The third view is the root view and has specified margin, which should be applied based
-        // on its direction.
-        verifyChange(thirdViewInfo, true, lp -> {
-            assertThat(lp.getMarginStart()).isEqualTo(0);
-            assertThat(lp.getMarginEnd()).isEqualTo(complicationMargin);
-            assertThat(lp.topMargin).isEqualTo(0);
-            assertThat(lp.bottomMargin).isEqualTo(0);
+        // Verify neither max height nor max width set.
+        verifyChange(view, false, lp -> {
+            assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+            assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
         });
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index ce7561e..fdb4cc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -97,35 +97,10 @@
     }
 
     /**
-     * Ensures ComplicationLayoutParams correctly returns whether the complication specified margin.
-     */
-    @Test
-    public void testIsMarginSpecified() {
-        final ComplicationLayoutParams paramsNoMargin = new ComplicationLayoutParams(
-                100,
-                100,
-                ComplicationLayoutParams.POSITION_TOP
-                        | ComplicationLayoutParams.POSITION_START,
-                ComplicationLayoutParams.DIRECTION_DOWN,
-                0);
-        assertThat(paramsNoMargin.isMarginSpecified()).isFalse();
-
-        final ComplicationLayoutParams paramsWithMargin = new ComplicationLayoutParams(
-                100,
-                100,
-                ComplicationLayoutParams.POSITION_TOP
-                        | ComplicationLayoutParams.POSITION_START,
-                ComplicationLayoutParams.DIRECTION_DOWN,
-                0,
-                20 /*margin*/);
-        assertThat(paramsWithMargin.isMarginSpecified()).isTrue();
-    }
-
-    /**
      * Ensures unspecified margin uses default.
      */
     @Test
-    public void testUnspecifiedMarginUsesDefault() {
+    public void testDefaultMargin() {
         final ComplicationLayoutParams params = new ComplicationLayoutParams(
                 100,
                 100,
@@ -161,13 +136,15 @@
                 ComplicationLayoutParams.POSITION_TOP,
                 ComplicationLayoutParams.DIRECTION_DOWN,
                 3,
-                10);
+                10,
+                20);
         final ComplicationLayoutParams copy = new ComplicationLayoutParams(params);
 
         assertThat(copy.getDirection() == params.getDirection()).isTrue();
         assertThat(copy.getPosition() == params.getPosition()).isTrue();
         assertThat(copy.getWeight() == params.getWeight()).isTrue();
         assertThat(copy.getMargin(0) == params.getMargin(1)).isTrue();
+        assertThat(copy.getConstraint() == params.getConstraint()).isTrue();
         assertThat(copy.height == params.height).isTrue();
         assertThat(copy.width == params.width).isTrue();
     }
@@ -193,4 +170,31 @@
         assertThat(copy.height == params.height).isTrue();
         assertThat(copy.width == params.width).isTrue();
     }
+
+    /**
+     * Ensures that constraint is set correctly.
+     */
+    @Test
+    public void testConstraint() {
+        final ComplicationLayoutParams paramsWithoutConstraint = new ComplicationLayoutParams(
+                100,
+                100,
+                ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                3,
+                10);
+        assertThat(paramsWithoutConstraint.constraintSpecified()).isFalse();
+
+        final int constraint = 10;
+        final ComplicationLayoutParams paramsWithConstraint = new ComplicationLayoutParams(
+                100,
+                100,
+                ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                3,
+                10,
+                constraint);
+        assertThat(paramsWithConstraint.constraintSpecified()).isTrue();
+        assertThat(paramsWithConstraint.getConstraint()).isEqualTo(constraint);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 30ad485..e6d3a69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -35,6 +35,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.controls.ControlsServiceInfo;
 import com.android.systemui.controls.controller.ControlsController;
@@ -84,7 +85,10 @@
     private ArgumentCaptor<ControlsListingController.ControlsListingCallback> mCallbackCaptor;
 
     @Mock
-    private ImageView mView;
+    private View mView;
+
+    @Mock
+    private ImageView mHomeControlsView;
 
     @Mock
     private ActivityStarter mActivityStarter;
@@ -105,6 +109,7 @@
         when(mControlsComponent.getControlsListingController()).thenReturn(
                 Optional.of(mControlsListingController));
         when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
+        when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
     }
 
     @Test
@@ -206,9 +211,9 @@
 
         final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
                 ArgumentCaptor.forClass(View.OnClickListener.class);
-        verify(mView).setOnClickListener(clickListenerCaptor.capture());
+        verify(mHomeControlsView).setOnClickListener(clickListenerCaptor.capture());
 
-        clickListenerCaptor.getValue().onClick(mView);
+        clickListenerCaptor.getValue().onClick(mHomeControlsView);
         verify(mUiEventLogger).log(
                 DreamHomeControlsComplication.DreamHomeControlsChipViewController
                         .DreamOverlayEvent.DREAM_HOME_CONTROLS_TAPPED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
index 32c5b3f..cef452b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -20,6 +20,7 @@
 import android.content.ContentValues
 import android.content.pm.PackageManager
 import android.content.pm.ProviderInfo
+import android.os.UserHandle
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SystemUIAppComponentFactoryBase
@@ -27,8 +28,10 @@
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -74,8 +77,8 @@
 
         underTest = KeyguardQuickAffordanceProvider()
         val scope = CoroutineScope(IMMEDIATE)
-        val selectionManager =
-            KeyguardQuickAffordanceSelectionManager(
+        val localUserSelectionManager =
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
@@ -91,11 +94,20 @@
                 userTracker = userTracker,
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
+        val remoteUserSelectionManager =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = scope,
+                userTracker = userTracker,
+                clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+                userHandle = UserHandle.SYSTEM,
+            )
         val quickAffordanceRepository =
             KeyguardQuickAffordanceRepository(
                 appContext = context,
                 scope = scope,
-                selectionManager = selectionManager,
+                localUserSelectionManager = localUserSelectionManager,
+                remoteUserSelectionManager = remoteUserSelectionManager,
+                userTracker = userTracker,
                 configs =
                     setOf(
                         FakeKeyguardQuickAffordanceConfig(
@@ -114,9 +126,10 @@
                         scope = scope,
                         backgroundDispatcher = IMMEDIATE,
                         secureSettings = FakeSettings(),
-                        selectionsManager = selectionManager,
+                        selectionsManager = localUserSelectionManager,
                     ),
                 dumpManager = mock(),
+                userHandle = UserHandle.SYSTEM,
             )
         underTest.interactor =
             KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index cda7018..9fa7db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.statusbar.policy.FlashlightController
 import com.android.systemui.utils.leaks.FakeFlashlightController
 import com.android.systemui.utils.leaks.LeakCheckedTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -38,156 +39,177 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(JUnit4::class)
 class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
 
     @Mock private lateinit var context: Context
     private lateinit var flashlightController: FakeFlashlightController
-    private lateinit var underTest : FlashlightQuickAffordanceConfig
+    private lateinit var underTest: FlashlightQuickAffordanceConfig
 
     @Before
     fun setUp() {
         injectLeakCheckedDependency(FlashlightController::class.java)
         MockitoAnnotations.initMocks(this)
 
-        flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController
+        flashlightController =
+            SysuiLeakCheck().getLeakChecker(FlashlightController::class.java)
+                as FakeFlashlightController
         underTest = FlashlightQuickAffordanceConfig(context, flashlightController)
     }
 
     @Test
     fun `flashlight is off -- triggered -- icon is on and active`() = runTest {
-        //given
+        // given
         flashlightController.isEnabled = false
         flashlightController.isAvailable = true
         val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
-        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
 
-        //when
+        // when
         underTest.onTriggered(null)
         val lastValue = values.last()
 
-        //then
+        // then
         assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
-        assertEquals(R.drawable.ic_flashlight_on,
-                ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
+        assertEquals(
+            R.drawable.qs_flashlight_icon_on,
+            ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+                    as? Icon.Resource)
+                ?.res
+        )
         job.cancel()
     }
 
     @Test
     fun `flashlight is on -- triggered -- icon is off and inactive`() = runTest {
-        //given
+        // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = true
         val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
-        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
 
-        //when
+        // when
         underTest.onTriggered(null)
         val lastValue = values.last()
 
-        //then
+        // then
         assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
-        assertEquals(R.drawable.ic_flashlight_off,
-                ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
+        assertEquals(
+            R.drawable.qs_flashlight_icon_off,
+            ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+                    as? Icon.Resource)
+                ?.res
+        )
         job.cancel()
     }
 
     @Test
     fun `flashlight is on -- receives error -- icon is off and inactive`() = runTest {
-        //given
+        // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = false
         val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
-        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
 
-        //when
+        // when
         flashlightController.onFlashlightError()
         val lastValue = values.last()
 
-        //then
+        // then
         assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
-        assertEquals(R.drawable.ic_flashlight_off,
-                ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
+        assertEquals(
+            R.drawable.qs_flashlight_icon_off,
+            ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+                    as? Icon.Resource)
+                ?.res
+        )
         job.cancel()
     }
 
     @Test
     fun `flashlight availability now off -- hidden`() = runTest {
-        //given
+        // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = false
         val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
-        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
 
-        //when
+        // when
         flashlightController.onFlashlightAvailabilityChanged(false)
         val lastValue = values.last()
 
-        //then
+        // then
         assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
         job.cancel()
     }
 
     @Test
     fun `flashlight availability now on -- flashlight on -- inactive and icon off`() = runTest {
-        //given
+        // given
         flashlightController.isEnabled = true
         flashlightController.isAvailable = false
         val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
-        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
 
-        //when
+        // when
         flashlightController.onFlashlightAvailabilityChanged(true)
         val lastValue = values.last()
 
-        //then
+        // then
         assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
-        assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active)
-        assertEquals(R.drawable.ic_flashlight_on, (lastValue.icon as? Icon.Resource)?.res)
+        assertTrue(
+            (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
+                is ActivationState.Active
+        )
+        assertEquals(R.drawable.qs_flashlight_icon_on, (lastValue.icon as? Icon.Resource)?.res)
         job.cancel()
     }
 
     @Test
     fun `flashlight availability now on -- flashlight off -- inactive and icon off`() = runTest {
-        //given
+        // given
         flashlightController.isEnabled = false
         flashlightController.isAvailable = false
         val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
-        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+        val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
 
-        //when
+        // when
         flashlightController.onFlashlightAvailabilityChanged(true)
         val lastValue = values.last()
 
-        //then
+        // then
         assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
-        assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive)
-        assertEquals(R.drawable.ic_flashlight_off, (lastValue.icon as? Icon.Resource)?.res)
+        assertTrue(
+            (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
+                is ActivationState.Inactive
+        )
+        assertEquals(R.drawable.qs_flashlight_icon_off, (lastValue.icon as? Icon.Resource)?.res)
         job.cancel()
     }
 
     @Test
     fun `flashlight available -- picker state default`() = runTest {
-        //given
+        // given
         flashlightController.isAvailable = true
 
-        //when
+        // when
         val result = underTest.getPickerScreenState()
 
-        //then
+        // then
         assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.Default)
     }
 
     @Test
     fun `flashlight not available -- picker state unavailable`() = runTest {
-        //given
+        // given
         flashlightController.isAvailable = false
 
-        //when
+        // when
         val result = underTest.getPickerScreenState()
 
-        //then
+        // then
         assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 552b8cb..3b0169d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -57,7 +57,7 @@
 
     private lateinit var testScope: TestScope
     private lateinit var testDispatcher: TestDispatcher
-    private lateinit var selectionManager: KeyguardQuickAffordanceSelectionManager
+    private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
     private lateinit var settings: FakeSettings
 
     @Before
@@ -75,7 +75,7 @@
         testDispatcher = UnconfinedTestDispatcher()
         testScope = TestScope(testDispatcher)
         selectionManager =
-            KeyguardQuickAffordanceSelectionManager(
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager =
                     mock {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 6a2376b..67091a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -52,11 +52,11 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(JUnit4::class)
-class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
+class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
 
     @Mock private lateinit var userFileManager: UserFileManager
 
-    private lateinit var underTest: KeyguardQuickAffordanceSelectionManager
+    private lateinit var underTest: KeyguardQuickAffordanceLocalUserSelectionManager
 
     private lateinit var userTracker: FakeUserTracker
     private lateinit var sharedPrefs: MutableMap<Int, SharedPreferences>
@@ -74,7 +74,7 @@
         Dispatchers.setMain(dispatcher)
 
         underTest =
-            KeyguardQuickAffordanceSelectionManager(
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager = userFileManager,
                 userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
new file mode 100644
index 0000000..d7e9cf1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
+
+    @Mock private lateinit var userHandle: UserHandle
+
+    private lateinit var underTest: KeyguardQuickAffordanceRemoteUserSelectionManager
+
+    private lateinit var clientFactory: FakeKeyguardQuickAffordanceProviderClientFactory
+    private lateinit var testScope: TestScope
+    private lateinit var testDispatcher: TestDispatcher
+    private lateinit var userTracker: FakeUserTracker
+    private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
+    private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
+        whenever(userHandle.isSystem).thenReturn(true)
+        client1 = FakeKeyguardQuickAffordanceProviderClient()
+        client2 = FakeKeyguardQuickAffordanceProviderClient()
+
+        userTracker = FakeUserTracker()
+        userTracker.set(
+            userInfos =
+                listOf(
+                    UserInfo(
+                        UserHandle.USER_SYSTEM,
+                        "Primary",
+                        /* flags= */ 0,
+                    ),
+                    UserInfo(
+                        OTHER_USER_ID_1,
+                        "Secondary 1",
+                        /* flags= */ 0,
+                    ),
+                    UserInfo(
+                        OTHER_USER_ID_2,
+                        "Secondary 2",
+                        /* flags= */ 0,
+                    ),
+                ),
+            selectedUserIndex = 0,
+        )
+
+        clientFactory =
+            FakeKeyguardQuickAffordanceProviderClientFactory(
+                userTracker,
+            ) { selectedUserId ->
+                when (selectedUserId) {
+                    OTHER_USER_ID_1 -> client1
+                    OTHER_USER_ID_2 -> client2
+                    else -> error("No client set-up for user $selectedUserId!")
+                }
+            }
+
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+
+        underTest =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = testScope.backgroundScope,
+                userTracker = userTracker,
+                clientFactory = clientFactory,
+                userHandle = userHandle,
+            )
+    }
+
+    @Test
+    fun `selections - primary user process`() =
+        testScope.runTest {
+            val values = mutableListOf<Map<String, List<String>>>()
+            val job = launch { underTest.selections.toList(values) }
+
+            runCurrent()
+            assertThat(values.last()).isEmpty()
+
+            client1.insertSelection(
+                slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+            )
+            client2.insertSelection(
+                slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+            )
+
+            userTracker.set(
+                userInfos = userTracker.userProfiles,
+                selectedUserIndex = 1,
+            )
+            runCurrent()
+            assertThat(values.last())
+                .isEqualTo(
+                    mapOf(
+                        KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+                            listOf(
+                                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+                            ),
+                    )
+                )
+
+            userTracker.set(
+                userInfos = userTracker.userProfiles,
+                selectedUserIndex = 2,
+            )
+            runCurrent()
+            assertThat(values.last())
+                .isEqualTo(
+                    mapOf(
+                        KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
+                            listOf(
+                                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+                            ),
+                    )
+                )
+
+            job.cancel()
+        }
+
+    @Test
+    fun `selections - secondary user process - always empty`() =
+        testScope.runTest {
+            whenever(userHandle.isSystem).thenReturn(false)
+            val values = mutableListOf<Map<String, List<String>>>()
+            val job = launch { underTest.selections.toList(values) }
+
+            runCurrent()
+            assertThat(values.last()).isEmpty()
+
+            client1.insertSelection(
+                slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+            )
+            userTracker.set(
+                userInfos = userTracker.userProfiles,
+                selectedUserIndex = 1,
+            )
+            runCurrent()
+            assertThat(values.last()).isEmpty()
+
+            job.cancel()
+        }
+
+    @Test
+    fun setSelections() =
+        testScope.runTest {
+            userTracker.set(
+                userInfos = userTracker.userProfiles,
+                selectedUserIndex = 1,
+            )
+            runCurrent()
+
+            underTest.setSelections(
+                slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
+            )
+            runCurrent()
+
+            assertThat(underTest.getSelections())
+                .isEqualTo(
+                    mapOf(
+                        KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+                            listOf(
+                                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+                            ),
+                    )
+                )
+        }
+
+    companion object {
+        private const val OTHER_USER_ID_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
+        private const val OTHER_USER_ID_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 652fae9..c40488a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -17,17 +17,23 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.content.pm.UserInfo
+import android.os.UserHandle
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -39,6 +45,7 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -55,14 +62,24 @@
 
     private lateinit var config1: FakeKeyguardQuickAffordanceConfig
     private lateinit var config2: FakeKeyguardQuickAffordanceConfig
+    private lateinit var userTracker: FakeUserTracker
+    private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
+    private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
 
     @Before
     fun setUp() {
-        config1 = FakeKeyguardQuickAffordanceConfig("built_in:1")
-        config2 = FakeKeyguardQuickAffordanceConfig("built_in:2")
+        config1 =
+            FakeKeyguardQuickAffordanceConfig(
+                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
+            )
+        config2 =
+            FakeKeyguardQuickAffordanceConfig(
+                FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
+            )
         val scope = CoroutineScope(IMMEDIATE)
-        val selectionManager =
-            KeyguardQuickAffordanceSelectionManager(
+        userTracker = FakeUserTracker()
+        val localUserSelectionManager =
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
@@ -75,24 +92,45 @@
                             )
                             .thenReturn(FakeSharedPreferences())
                     },
-                userTracker = FakeUserTracker(),
+                userTracker = userTracker,
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
+        client1 = FakeKeyguardQuickAffordanceProviderClient()
+        client2 = FakeKeyguardQuickAffordanceProviderClient()
+        val remoteUserSelectionManager =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = scope,
+                userTracker = userTracker,
+                clientFactory =
+                    FakeKeyguardQuickAffordanceProviderClientFactory(
+                        userTracker,
+                    ) { selectedUserId ->
+                        when (selectedUserId) {
+                            SECONDARY_USER_1 -> client1
+                            SECONDARY_USER_2 -> client2
+                            else -> error("No set-up client for user $selectedUserId!")
+                        }
+                    },
+                userHandle = UserHandle.SYSTEM,
+            )
 
         underTest =
             KeyguardQuickAffordanceRepository(
                 appContext = context,
                 scope = scope,
-                selectionManager = selectionManager,
+                localUserSelectionManager = localUserSelectionManager,
+                remoteUserSelectionManager = remoteUserSelectionManager,
+                userTracker = userTracker,
                 legacySettingSyncer =
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = scope,
                         backgroundDispatcher = IMMEDIATE,
                         secureSettings = FakeSettings(),
-                        selectionsManager = selectionManager,
+                        selectionsManager = localUserSelectionManager,
                     ),
                 configs = setOf(config1, config2),
                 dumpManager = mock(),
+                userHandle = UserHandle.SYSTEM,
             )
     }
 
@@ -187,7 +225,53 @@
             )
     }
 
-    private suspend fun assertSelections(
+    @Test
+    fun `selections for secondary user`() =
+        runBlocking(IMMEDIATE) {
+            userTracker.set(
+                userInfos =
+                    listOf(
+                        UserInfo(
+                            UserHandle.USER_SYSTEM,
+                            "Primary",
+                            /* flags= */ 0,
+                        ),
+                        UserInfo(
+                            SECONDARY_USER_1,
+                            "Secondary 1",
+                            /* flags= */ 0,
+                        ),
+                        UserInfo(
+                            SECONDARY_USER_2,
+                            "Secondary 2",
+                            /* flags= */ 0,
+                        ),
+                    ),
+                selectedUserIndex = 2,
+            )
+            client2.insertSelection(
+                slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+            )
+            val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
+            val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
+            yield()
+
+            assertSelections(
+                observed = observed.last(),
+                expected =
+                    mapOf(
+                        KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+                            listOf(
+                                config2,
+                            ),
+                    )
+            )
+
+            job.cancel()
+        }
+
+    private fun assertSelections(
         observed: Map<String, List<KeyguardQuickAffordanceConfig>>?,
         expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
     ) {
@@ -201,5 +285,7 @@
 
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
+        private const val SECONDARY_USER_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
+        private const val SECONDARY_USER_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 13fc9fc..5deac19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -16,10 +16,13 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
 import com.android.systemui.common.shared.model.Position
 import com.android.systemui.doze.DozeHost
 import com.android.systemui.doze.DozeMachine
@@ -27,9 +30,11 @@
 import com.android.systemui.doze.DozeTransitionListener
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -57,9 +62,10 @@
     @Mock private lateinit var dozeHost: DozeHost
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
-    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
     @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
+    @Mock private lateinit var authController: AuthController
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
     private lateinit var underTest: KeyguardRepositoryImpl
 
@@ -76,6 +82,7 @@
                 keyguardStateController,
                 keyguardUpdateMonitor,
                 dozeTransitionListener,
+                authController,
             )
     }
 
@@ -198,7 +205,7 @@
     fun dozeAmount() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<Float>()
-            val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
+            val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)
 
             val captor = argumentCaptor<StatusBarStateController.StateListener>()
             verify(statusBarStateController).addCallback(captor.capture())
@@ -207,7 +214,7 @@
             captor.value.onDozeAmountChanged(0.498f, 0.5f)
             captor.value.onDozeAmountChanged(0.661f, 0.65f)
 
-            assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
+            assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
 
             job.cancel()
             verify(statusBarStateController).removeCallback(captor.value)
@@ -217,25 +224,36 @@
     fun wakefulness() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<WakefulnessModel>()
-            val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+            val job = underTest.wakefulness.onEach(values::add).launchIn(this)
 
             val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
             verify(wakefulnessLifecycle).addObserver(captor.capture())
 
+            whenever(wakefulnessLifecycle.wakefulness)
+                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
             captor.value.onStartedWakingUp()
+
+            whenever(wakefulnessLifecycle.wakefulness)
+                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
             captor.value.onFinishedWakingUp()
+
+            whenever(wakefulnessLifecycle.wakefulness)
+                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
             captor.value.onStartedGoingToSleep()
+
+            whenever(wakefulnessLifecycle.wakefulness)
+                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
             captor.value.onFinishedGoingToSleep()
 
-            assertThat(values)
+            assertThat(values.map { it.state })
                 .isEqualTo(
                     listOf(
                         // Initial value will be ASLEEP
-                        WakefulnessModel.ASLEEP,
-                        WakefulnessModel.STARTING_TO_WAKE,
-                        WakefulnessModel.AWAKE,
-                        WakefulnessModel.STARTING_TO_SLEEP,
-                        WakefulnessModel.ASLEEP,
+                        WakefulnessState.ASLEEP,
+                        WakefulnessState.STARTING_TO_WAKE,
+                        WakefulnessState.AWAKE,
+                        WakefulnessState.STARTING_TO_SLEEP,
+                        WakefulnessState.ASLEEP,
                     )
                 )
 
@@ -329,14 +347,20 @@
             val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
             verify(biometricUnlockController).addBiometricModeListener(captor.capture())
 
-            captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
-            captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+            listOf(
+                    BiometricUnlockController.MODE_NONE,
+                    BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
+                    BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
+                    BiometricUnlockController.MODE_SHOW_BOUNCER,
+                    BiometricUnlockController.MODE_ONLY_WAKE,
+                    BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
+                    BiometricUnlockController.MODE_DISMISS_BOUNCER,
+                    BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
+                )
+                .forEach {
+                    whenever(biometricUnlockController.mode).thenReturn(it)
+                    captor.value.onModeChanged(it)
+                }
 
             assertThat(values)
                 .isEqualTo(
@@ -420,4 +444,104 @@
             job.cancel()
             verify(dozeTransitionListener).removeCallback(listener)
         }
+
+    @Test
+    fun fingerprintSensorLocation() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Point?>()
+            val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
+
+            val captor = argumentCaptor<AuthController.Callback>()
+            verify(authController).addCallback(captor.capture())
+
+            // An initial, null value should be initially emitted so that flows combined with this
+            // one
+            // emit values immediately. The sensor location is expected to be nullable, so anyone
+            // consuming it should handle that properly.
+            assertThat(values).isEqualTo(listOf(null))
+
+            listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
+                .onEach {
+                    whenever(authController.fingerprintSensorLocation).thenReturn(it)
+                    captor.value.onFingerprintLocationChanged()
+                }
+                .also { dispatchedSensorLocations ->
+                    assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+                }
+
+            job.cancel()
+        }
+
+    @Test
+    fun faceSensorLocation() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Point?>()
+            val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
+
+            val captor = argumentCaptor<AuthController.Callback>()
+            verify(authController).addCallback(captor.capture())
+
+            // An initial, null value should be initially emitted so that flows combined with this
+            // one
+            // emit values immediately. The sensor location is expected to be nullable, so anyone
+            // consuming it should handle that properly.
+            assertThat(values).isEqualTo(listOf(null))
+
+            listOf(
+                    Point(500, 500),
+                    Point(0, 0),
+                    null,
+                    Point(250, 250),
+                )
+                .onEach {
+                    whenever(authController.faceSensorLocation).thenReturn(it)
+                    captor.value.onFaceSensorLocationChanged()
+                }
+                .also { dispatchedSensorLocations ->
+                    assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+                }
+
+            job.cancel()
+        }
+
+    @Test
+    fun biometricUnlockSource() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<BiometricUnlockSource?>()
+            val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)
+
+            val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+            verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+            // An initial, null value should be initially emitted so that flows combined with this
+            // one
+            // emit values immediately. The biometric unlock source is expected to be nullable, so
+            // anyone consuming it should handle that properly.
+            assertThat(values).isEqualTo(listOf(null))
+
+            listOf(
+                    BiometricSourceType.FINGERPRINT,
+                    BiometricSourceType.IRIS,
+                    null,
+                    BiometricSourceType.FACE,
+                    BiometricSourceType.FINGERPRINT,
+                )
+                .onEach { biometricSourceType ->
+                    captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
+                }
+
+            assertThat(values)
+                .isEqualTo(
+                    listOf(
+                        null,
+                        BiometricUnlockSource.FINGERPRINT_SENSOR,
+                        BiometricUnlockSource.FACE_SENSOR,
+                        null,
+                        BiometricUnlockSource.FACE_SENSOR,
+                        BiometricUnlockSource.FINGERPRINT_SENSOR,
+                    )
+                )
+
+            job.cancel()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
new file mode 100644
index 0000000..d2db910
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.graphics.Point
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimRepositoryTest : SysuiTestCase() {
+    private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
+    private lateinit var underTest: LightRevealScrimRepositoryImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        fakeKeyguardRepository = FakeKeyguardRepository()
+        underTest = LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context)
+    }
+
+    @Test
+    fun `nextRevealEffect - effect switches between default and biometric with no dupes`() =
+        runTest {
+            val values = mutableListOf<LightRevealEffect>()
+            val job = launch { underTest.revealEffect.collect { values.add(it) } }
+
+            // We should initially emit the default reveal effect.
+            runCurrent()
+            values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
+
+            // The source and sensor locations are still null, so we should still be using the
+            // default reveal despite a biometric unlock.
+            fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+
+            runCurrent()
+            values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+            // We got a source but still have no sensor locations, so should be sticking with
+            // the default effect.
+            fakeKeyguardRepository.setBiometricUnlockSource(
+                BiometricUnlockSource.FINGERPRINT_SENSOR
+            )
+
+            runCurrent()
+            values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+            // We got a location for the face sensor, but we unlocked with fingerprint.
+            val faceLocation = Point(250, 0)
+            fakeKeyguardRepository.setFaceSensorLocation(faceLocation)
+
+            runCurrent()
+            values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+            // Now we have fingerprint sensor locations, and wake and unlock via fingerprint.
+            val fingerprintLocation = Point(500, 500)
+            fakeKeyguardRepository.setFingerprintSensorLocation(fingerprintLocation)
+            fakeKeyguardRepository.setBiometricUnlockSource(
+                BiometricUnlockSource.FINGERPRINT_SENSOR
+            )
+            fakeKeyguardRepository.setBiometricUnlockState(
+                BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+            )
+
+            // We should now have switched to the circle reveal, at the fingerprint location.
+            runCurrent()
+            values.assertEffectsMatchPredicates(
+                { it == DEFAULT_REVEAL_EFFECT },
+                {
+                    it is CircleReveal &&
+                        it.centerX == fingerprintLocation.x &&
+                        it.centerY == fingerprintLocation.y
+                },
+            )
+
+            // Subsequent wake and unlocks should not emit duplicate, identical CircleReveals.
+            val valuesPrevSize = values.size
+            fakeKeyguardRepository.setBiometricUnlockState(
+                BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+            )
+            fakeKeyguardRepository.setBiometricUnlockState(
+                BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+            )
+            assertEquals(valuesPrevSize, values.size)
+
+            // Non-biometric unlock, we should return to the default reveal.
+            fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+
+            runCurrent()
+            values.assertEffectsMatchPredicates(
+                { it == DEFAULT_REVEAL_EFFECT },
+                {
+                    it is CircleReveal &&
+                        it.centerX == fingerprintLocation.x &&
+                        it.centerY == fingerprintLocation.y
+                },
+                { it == DEFAULT_REVEAL_EFFECT },
+            )
+
+            // We already have a face location, so switching to face source should update the
+            // CircleReveal.
+            fakeKeyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FACE_SENSOR)
+            runCurrent()
+            fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+            runCurrent()
+
+            values.assertEffectsMatchPredicates(
+                { it == DEFAULT_REVEAL_EFFECT },
+                {
+                    it is CircleReveal &&
+                        it.centerX == fingerprintLocation.x &&
+                        it.centerY == fingerprintLocation.y
+                },
+                { it == DEFAULT_REVEAL_EFFECT },
+                {
+                    it is CircleReveal &&
+                        it.centerX == faceLocation.x &&
+                        it.centerY == faceLocation.y
+                },
+            )
+
+            job.cancel()
+        }
+
+    /**
+     * Asserts that the list of LightRevealEffects satisfies the list of predicates, in order, with
+     * no leftover elements.
+     */
+    private fun List<LightRevealEffect>.assertEffectsMatchPredicates(
+        vararg predicates: (LightRevealEffect) -> Boolean
+    ) {
+        println(this)
+        assertEquals(predicates.size, this.size)
+
+        assertFalse(
+            zip(predicates) { effect, predicate -> predicate(effect) }.any { matched -> !matched }
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index ba7c40b..1c1f039 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.content.Intent
+import android.os.UserHandle
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
@@ -29,9 +30,11 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -237,8 +240,8 @@
         val qrCodeScanner =
             FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
         val scope = CoroutineScope(IMMEDIATE)
-        val selectionManager =
-            KeyguardQuickAffordanceSelectionManager(
+        val localUserSelectionManager =
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
@@ -254,20 +257,30 @@
                 userTracker = userTracker,
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
+        val remoteUserSelectionManager =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = scope,
+                userTracker = userTracker,
+                clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+                userHandle = UserHandle.SYSTEM,
+            )
         val quickAffordanceRepository =
             KeyguardQuickAffordanceRepository(
                 appContext = context,
                 scope = scope,
-                selectionManager = selectionManager,
+                localUserSelectionManager = localUserSelectionManager,
+                remoteUserSelectionManager = remoteUserSelectionManager,
+                userTracker = userTracker,
                 legacySettingSyncer =
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = scope,
                         backgroundDispatcher = IMMEDIATE,
                         secureSettings = FakeSettings(),
-                        selectionsManager = selectionManager,
+                        selectionsManager = localUserSelectionManager,
                     ),
                 configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
                 dumpManager = mock(),
+                userHandle = UserHandle.SYSTEM,
             )
         underTest =
             KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 8d0c4ef..11fe905 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.os.UserHandle
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
@@ -26,9 +27,11 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -98,8 +101,8 @@
             FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
         val scope = CoroutineScope(IMMEDIATE)
 
-        val selectionManager =
-            KeyguardQuickAffordanceSelectionManager(
+        val localUserSelectionManager =
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
@@ -115,20 +118,30 @@
                 userTracker = userTracker,
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
+        val remoteUserSelectionManager =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = scope,
+                userTracker = userTracker,
+                clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+                userHandle = UserHandle.SYSTEM,
+            )
         val quickAffordanceRepository =
             KeyguardQuickAffordanceRepository(
                 appContext = context,
                 scope = scope,
-                selectionManager = selectionManager,
+                localUserSelectionManager = localUserSelectionManager,
+                remoteUserSelectionManager = remoteUserSelectionManager,
+                userTracker = userTracker,
                 legacySettingSyncer =
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = scope,
                         backgroundDispatcher = IMMEDIATE,
                         secureSettings = FakeSettings(),
-                        selectionsManager = selectionManager,
+                        selectionsManager = localUserSelectionManager,
                     ),
                 configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
                 dumpManager = mock(),
+                userHandle = UserHandle.SYSTEM,
             )
         featureFlags =
             FakeFeatureFlags().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
new file mode 100644
index 0000000..3166214
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimInteractorTest : SysuiTestCase() {
+    private val fakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository()
+    private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
+
+    private val keyguardTransitionInteractor =
+        KeyguardTransitionInteractor(fakeKeyguardTransitionRepository)
+
+    private lateinit var underTest: LightRevealScrimInteractor
+
+    private val reveal1 =
+        object : LightRevealEffect {
+            override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+        }
+
+    private val reveal2 =
+        object : LightRevealEffect {
+            override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+        }
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            LightRevealScrimInteractor(
+                fakeKeyguardTransitionRepository,
+                keyguardTransitionInteractor,
+                fakeLightRevealScrimRepository
+            )
+    }
+
+    @Test
+    fun `lightRevealEffect - does not change during keyguard transition`() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<LightRevealEffect>()
+            val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
+
+            fakeLightRevealScrimRepository.setRevealEffect(reveal1)
+
+            // The reveal effect shouldn't emit anything until a keyguard transition starts.
+            assertEquals(values.size, 0)
+
+            // Once it starts, it should emit reveal1.
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(transitionState = TransitionState.STARTED)
+            )
+            assertEquals(values, listOf(reveal1))
+
+            // Until the next transition starts, reveal2 should not be emitted.
+            fakeLightRevealScrimRepository.setRevealEffect(reveal2)
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(transitionState = TransitionState.RUNNING)
+            )
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(transitionState = TransitionState.FINISHED)
+            )
+            assertEquals(values, listOf(reveal1))
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(transitionState = TransitionState.STARTED)
+            )
+            assertEquals(values, listOf(reveal1, reveal2))
+
+            job.cancel()
+        }
+
+    @Test
+    fun `revealAmount - inverted when appropriate`() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+            val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.LOCKSCREEN,
+                    value = 0.3f
+                )
+            )
+
+            assertEquals(values, listOf(0.3f))
+
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    value = 0.3f
+                )
+            )
+
+            assertEquals(values, listOf(0.3f, 0.7f))
+
+            job.cancel()
+        }
+
+    @Test
+    fun `revealAmount - ignores transitions that do not affect reveal amount`() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+            val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(from = KeyguardState.DOZING, to = KeyguardState.AOD, value = 0.3f)
+            )
+
+            assertEquals(values, emptyList<Float>())
+
+            fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(from = KeyguardState.AOD, to = KeyguardState.DOZING, value = 0.3f)
+            )
+
+            assertEquals(values, emptyList<Float>())
+
+            job.cancel()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 32849cd..83a5d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.content.Intent
+import android.os.UserHandle
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
@@ -27,9 +28,11 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -121,8 +124,8 @@
         whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
         val scope = CoroutineScope(IMMEDIATE)
-        val selectionManager =
-            KeyguardQuickAffordanceSelectionManager(
+        val localUserSelectionManager =
+            KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
@@ -138,17 +141,26 @@
                 userTracker = userTracker,
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
+        val remoteUserSelectionManager =
+            KeyguardQuickAffordanceRemoteUserSelectionManager(
+                scope = scope,
+                userTracker = userTracker,
+                clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+                userHandle = UserHandle.SYSTEM,
+            )
         val quickAffordanceRepository =
             KeyguardQuickAffordanceRepository(
                 appContext = context,
                 scope = scope,
-                selectionManager = selectionManager,
+                localUserSelectionManager = localUserSelectionManager,
+                remoteUserSelectionManager = remoteUserSelectionManager,
+                userTracker = userTracker,
                 legacySettingSyncer =
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = scope,
                         backgroundDispatcher = IMMEDIATE,
                         secureSettings = FakeSettings(),
-                        selectionsManager = selectionManager,
+                        selectionsManager = localUserSelectionManager,
                     ),
                 configs =
                     setOf(
@@ -157,6 +169,7 @@
                         qrCodeScannerAffordanceConfig,
                     ),
                 dumpManager = mock(),
+                userHandle = UserHandle.SYSTEM,
             )
         underTest =
             KeyguardBottomAreaViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 688c66a..2c8d7ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -46,6 +46,109 @@
     }
 
     @Test
+    fun dumpChanges_hasHeader() {
+        val dumpedString = dumpChanges()
+
+        assertThat(logLines(dumpedString)[0]).isEqualTo(HEADER_PREFIX + NAME)
+    }
+
+    @Test
+    fun dumpChanges_hasVersion() {
+        val dumpedString = dumpChanges()
+
+        assertThat(logLines(dumpedString)[1]).isEqualTo("version $VERSION")
+    }
+
+    @Test
+    fun dumpChanges_hasFooter() {
+        val dumpedString = dumpChanges()
+
+        assertThat(logLines(dumpedString).last()).isEqualTo(FOOTER_PREFIX + NAME)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun dumpChanges_str_separatorNotAllowedInPrefix() {
+        val next =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("columnName", "stringValue")
+                }
+            }
+        underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun dumpChanges_bool_separatorNotAllowedInPrefix() {
+        val next =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("columnName", true)
+                }
+            }
+        underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun dumpChanges_int_separatorNotAllowedInPrefix() {
+        val next =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("columnName", 567)
+                }
+            }
+        underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun dumpChanges_str_separatorNotAllowedInColumnName() {
+        val next =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("column${SEPARATOR}Name", "stringValue")
+                }
+            }
+        underTest.logDiffs("prefix", TestDiffable(), next)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun dumpChanges_bool_separatorNotAllowedInColumnName() {
+        val next =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("column${SEPARATOR}Name", true)
+                }
+            }
+        underTest.logDiffs("prefix", TestDiffable(), next)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun dumpChanges_int_separatorNotAllowedInColumnName() {
+        val next =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("column${SEPARATOR}Name", 456)
+                }
+            }
+        underTest.logDiffs("prefix", TestDiffable(), next)
+    }
+
+    @Test
+    fun logChange_bool_dumpsCorrectly() {
+        systemClock.setCurrentTimeMillis(4000L)
+
+        underTest.logChange("prefix", "columnName", true)
+
+        val dumpedString = dumpChanges()
+        val expected =
+            TABLE_LOG_DATE_FORMAT.format(4000L) +
+                SEPARATOR +
+                "prefix.columnName" +
+                SEPARATOR +
+                "true"
+        assertThat(dumpedString).contains(expected)
+    }
+
+    @Test
     fun dumpChanges_strChange_logsFromNext() {
         systemClock.setCurrentTimeMillis(100L)
 
@@ -66,11 +169,14 @@
 
         val dumpedString = dumpChanges()
 
-        assertThat(dumpedString).contains("prefix")
-        assertThat(dumpedString).contains("stringValChange")
-        assertThat(dumpedString).contains("newStringVal")
+        val expected =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "prefix.stringValChange" +
+                SEPARATOR +
+                "newStringVal"
+        assertThat(dumpedString).contains(expected)
         assertThat(dumpedString).doesNotContain("prevStringVal")
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
     }
 
     @Test
@@ -94,11 +200,14 @@
 
         val dumpedString = dumpChanges()
 
-        assertThat(dumpedString).contains("prefix")
-        assertThat(dumpedString).contains("booleanValChange")
-        assertThat(dumpedString).contains("true")
+        val expected =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "prefix.booleanValChange" +
+                SEPARATOR +
+                "true"
+        assertThat(dumpedString).contains(expected)
         assertThat(dumpedString).doesNotContain("false")
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
     }
 
     @Test
@@ -122,11 +231,14 @@
 
         val dumpedString = dumpChanges()
 
-        assertThat(dumpedString).contains("prefix")
-        assertThat(dumpedString).contains("intValChange")
-        assertThat(dumpedString).contains("67890")
+        val expected =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "prefix.intValChange" +
+                SEPARATOR +
+                "67890"
+        assertThat(dumpedString).contains(expected)
         assertThat(dumpedString).doesNotContain("12345")
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
     }
 
     @Test
@@ -152,9 +264,9 @@
         val dumpedString = dumpChanges()
 
         // THEN the dump still works
-        assertThat(dumpedString).contains("booleanValChange")
-        assertThat(dumpedString).contains("true")
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
+        val expected =
+            TABLE_LOG_DATE_FORMAT.format(100L) + SEPARATOR + "booleanValChange" + SEPARATOR + "true"
+        assertThat(dumpedString).contains(expected)
     }
 
     @Test
@@ -186,15 +298,34 @@
 
         val dumpedString = dumpChanges()
 
-        assertThat(dumpedString).contains("valChange")
-        assertThat(dumpedString).contains("stateValue12")
-        assertThat(dumpedString).contains("stateValue20")
-        assertThat(dumpedString).contains("stateValue40")
-        assertThat(dumpedString).contains("stateValue45")
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(12000L))
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(20000L))
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(40000L))
-        assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(45000L))
+        val expected1 =
+            TABLE_LOG_DATE_FORMAT.format(12000L) +
+                SEPARATOR +
+                "valChange" +
+                SEPARATOR +
+                "stateValue12"
+        val expected2 =
+            TABLE_LOG_DATE_FORMAT.format(20000L) +
+                SEPARATOR +
+                "valChange" +
+                SEPARATOR +
+                "stateValue20"
+        val expected3 =
+            TABLE_LOG_DATE_FORMAT.format(40000L) +
+                SEPARATOR +
+                "valChange" +
+                SEPARATOR +
+                "stateValue40"
+        val expected4 =
+            TABLE_LOG_DATE_FORMAT.format(45000L) +
+                SEPARATOR +
+                "valChange" +
+                SEPARATOR +
+                "stateValue45"
+        assertThat(dumpedString).contains(expected1)
+        assertThat(dumpedString).contains(expected2)
+        assertThat(dumpedString).contains(expected3)
+        assertThat(dumpedString).contains(expected4)
     }
 
     @Test
@@ -214,10 +345,73 @@
 
         val dumpedString = dumpChanges()
 
-        assertThat(dumpedString).contains("status")
-        assertThat(dumpedString).contains("in progress")
-        assertThat(dumpedString).contains("connected")
-        assertThat(dumpedString).contains("false")
+        val timestamp = TABLE_LOG_DATE_FORMAT.format(100L)
+        val expected1 = timestamp + SEPARATOR + "status" + SEPARATOR + "in progress"
+        val expected2 = timestamp + SEPARATOR + "connected" + SEPARATOR + "false"
+        assertThat(dumpedString).contains(expected1)
+        assertThat(dumpedString).contains(expected2)
+    }
+
+    @Test
+    fun logChange_rowInitializer_dumpsCorrectly() {
+        systemClock.setCurrentTimeMillis(100L)
+
+        underTest.logChange("") { row ->
+            row.logChange("column1", "val1")
+            row.logChange("column2", 2)
+            row.logChange("column3", true)
+        }
+
+        val dumpedString = dumpChanges()
+
+        val timestamp = TABLE_LOG_DATE_FORMAT.format(100L)
+        val expected1 = timestamp + SEPARATOR + "column1" + SEPARATOR + "val1"
+        val expected2 = timestamp + SEPARATOR + "column2" + SEPARATOR + "2"
+        val expected3 = timestamp + SEPARATOR + "column3" + SEPARATOR + "true"
+        assertThat(dumpedString).contains(expected1)
+        assertThat(dumpedString).contains(expected2)
+        assertThat(dumpedString).contains(expected3)
+    }
+
+    @Test
+    fun logChangeAndLogDiffs_bothLogged() {
+        systemClock.setCurrentTimeMillis(100L)
+
+        underTest.logChange("") { row ->
+            row.logChange("column1", "val1")
+            row.logChange("column2", 2)
+            row.logChange("column3", true)
+        }
+
+        systemClock.setCurrentTimeMillis(200L)
+        val prevDiffable = object : TestDiffable() {}
+        val nextDiffable =
+            object : TestDiffable() {
+                override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+                    row.logChange("column1", "newVal1")
+                    row.logChange("column2", 222)
+                    row.logChange("column3", false)
+                }
+            }
+
+        underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable)
+
+        val dumpedString = dumpChanges()
+
+        val timestamp1 = TABLE_LOG_DATE_FORMAT.format(100L)
+        val expected1 = timestamp1 + SEPARATOR + "column1" + SEPARATOR + "val1"
+        val expected2 = timestamp1 + SEPARATOR + "column2" + SEPARATOR + "2"
+        val expected3 = timestamp1 + SEPARATOR + "column3" + SEPARATOR + "true"
+        val timestamp2 = TABLE_LOG_DATE_FORMAT.format(200L)
+        val expected4 = timestamp2 + SEPARATOR + "column1" + SEPARATOR + "newVal1"
+        val expected5 = timestamp2 + SEPARATOR + "column2" + SEPARATOR + "222"
+        val expected6 = timestamp2 + SEPARATOR + "column3" + SEPARATOR + "false"
+        assertThat(dumpedString).contains(expected1)
+        assertThat(dumpedString).contains(expected2)
+        assertThat(dumpedString).contains(expected3)
+        assertThat(dumpedString).contains(expected4)
+        assertThat(dumpedString).contains(expected5)
+        assertThat(dumpedString).contains(expected6)
     }
 
     @Test
@@ -247,14 +441,24 @@
     }
 
     private fun dumpChanges(): String {
-        underTest.dumpChanges(PrintWriter(outputWriter))
+        underTest.dump(PrintWriter(outputWriter), arrayOf())
         return outputWriter.toString()
     }
 
-    private abstract class TestDiffable : Diffable<TestDiffable> {
+    private fun logLines(string: String): List<String> {
+        return string.split("\n").filter { it.isNotBlank() }
+    }
+
+    private open class TestDiffable : Diffable<TestDiffable> {
         override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {}
     }
 }
 
 private const val NAME = "TestTableBuffer"
 private const val MAX_SIZE = 10
+
+// Copying these here from [TableLogBuffer] so that we catch any accidental versioning change
+private const val HEADER_PREFIX = "SystemUI StateChangeTableSection START: "
+private const val FOOTER_PREFIX = "SystemUI StateChangeTableSection END: "
+private const val SEPARATOR = "|" // TBD
+private const val VERSION = "1"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
index 84fdfd7..136ace1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.media.controls.models.player.MediaDeviceData
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
@@ -79,6 +80,7 @@
 class MediaResumeListenerTest : SysuiTestCase() {
 
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var mediaDataManager: MediaDataManager
     @Mock private lateinit var device: MediaDeviceData
     @Mock private lateinit var token: MediaSession.Token
@@ -131,12 +133,15 @@
         whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor)
         whenever(mockContext.packageManager).thenReturn(context.packageManager)
         whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
+        whenever(mockContext.userId).thenReturn(context.userId)
 
         executor = FakeExecutor(clock)
         resumeListener =
             MediaResumeListener(
                 mockContext,
                 broadcastDispatcher,
+                userTracker,
+                executor,
                 executor,
                 tunerService,
                 resumeBrowserFactory,
@@ -177,6 +182,8 @@
             MediaResumeListener(
                 context,
                 broadcastDispatcher,
+                userTracker,
+                executor,
                 executor,
                 tunerService,
                 resumeBrowserFactory,
@@ -185,7 +192,7 @@
             )
         listener.setManager(mediaDataManager)
         verify(broadcastDispatcher, never())
-            .registerReceiver(eq(listener.userChangeReceiver), any(), any(), any(), anyInt(), any())
+            .registerReceiver(eq(listener.userUnlockReceiver), any(), any(), any(), anyInt(), any())
 
         // When data is loaded, we do NOT execute or update anything
         listener.onMediaDataLoaded(KEY, OLD_KEY, data)
@@ -289,7 +296,7 @@
         resumeListener.setManager(mediaDataManager)
         verify(broadcastDispatcher)
             .registerReceiver(
-                eq(resumeListener.userChangeReceiver),
+                eq(resumeListener.userUnlockReceiver),
                 any(),
                 any(),
                 any(),
@@ -299,7 +306,8 @@
 
         // When we get an unlock event
         val intent = Intent(Intent.ACTION_USER_UNLOCKED)
-        resumeListener.userChangeReceiver.onReceive(context, intent)
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+        resumeListener.userUnlockReceiver.onReceive(context, intent)
 
         // Then we should attempt to find recent media for each saved component
         verify(resumeBrowser, times(3)).findRecentMedia()
@@ -375,6 +383,8 @@
             MediaResumeListener(
                 mockContext,
                 broadcastDispatcher,
+                userTracker,
+                executor,
                 executor,
                 tunerService,
                 resumeBrowserFactory,
@@ -386,7 +396,8 @@
 
         // When we load a component that was played recently
         val intent = Intent(Intent.ACTION_USER_UNLOCKED)
-        resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+        resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
 
         // We add its resume controls
         verify(resumeBrowser, times(1)).findRecentMedia()
@@ -404,6 +415,8 @@
             MediaResumeListener(
                 mockContext,
                 broadcastDispatcher,
+                userTracker,
+                executor,
                 executor,
                 tunerService,
                 resumeBrowserFactory,
@@ -415,7 +428,8 @@
 
         // When we load a component that is not recent
         val intent = Intent(Intent.ACTION_USER_UNLOCKED)
-        resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+        resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
 
         // We do not try to add resume controls
         verify(resumeBrowser, times(0)).findRecentMedia()
@@ -443,6 +457,8 @@
             MediaResumeListener(
                 mockContext,
                 broadcastDispatcher,
+                userTracker,
+                executor,
                 executor,
                 tunerService,
                 resumeBrowserFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index f43a34f..80adbf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -44,14 +44,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.telecom.TelecomManager;
 import android.testing.AndroidTestingRunner;
@@ -79,7 +76,6 @@
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
@@ -119,6 +115,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
+import java.util.concurrent.Executor;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -166,7 +163,7 @@
     @Mock
     private Handler mHandler;
     @Mock
-    private BroadcastDispatcher mBroadcastDispatcher;
+    private UserTracker mUserTracker;
     @Mock
     private UiEventLogger mUiEventLogger;
     @Mock
@@ -315,14 +312,10 @@
     }
 
     @Test
-    public void testRegisteredWithDispatcher() {
+    public void testRegisteredWithUserTracker() {
         mNavigationBar.init();
         mNavigationBar.onViewAttached();
-        verify(mBroadcastDispatcher).registerReceiverWithHandler(
-                any(BroadcastReceiver.class),
-                any(IntentFilter.class),
-                any(Handler.class),
-                any(UserHandle.class));
+        verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
     }
 
     @Test
@@ -463,7 +456,7 @@
                 mStatusBarStateController,
                 mStatusBarKeyguardViewManager,
                 mMockSysUiState,
-                mBroadcastDispatcher,
+                mUserTracker,
                 mCommandQueue,
                 Optional.of(mock(Pip.class)),
                 Optional.of(mock(Recents.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index c377c37..338182a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.power.PowerUI.WarningsUI;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -85,6 +86,7 @@
     private PowerUI mPowerUI;
     @Mock private EnhancedEstimates mEnhancedEstimates;
     @Mock private PowerManager mPowerManager;
+    @Mock private UserTracker mUserTracker;
     @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock private IThermalService mThermalServiceMock;
     private IThermalEventListener mUsbThermalEventListener;
@@ -682,7 +684,8 @@
     private void createPowerUi() {
         mPowerUI = new PowerUI(
                 mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
-                mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager);
+                mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager,
+                mUserTracker);
         mPowerUI.mThermalService = mThermalServiceMock;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 645b1cd..23466cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestCoroutineScheduler
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -55,7 +56,7 @@
 
     @Before
     fun setUp() {
-        utils = FooterActionsTestUtils(context, TestableLooper.get(this))
+        utils = FooterActionsTestUtils(context, TestableLooper.get(this), TestCoroutineScheduler())
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 081a218..47afa70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.FakeFgsManagerController
 import com.android.systemui.qs.QSSecurityFooterUtils
 import com.android.systemui.qs.footer.FooterActionsTestUtils
@@ -44,12 +45,9 @@
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -62,16 +60,20 @@
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
 class FooterActionsViewModelTest : SysuiTestCase() {
+    private val testScope = TestScope()
     private lateinit var utils: FooterActionsTestUtils
-    private val testDispatcher = UnconfinedTestDispatcher(TestCoroutineScheduler())
 
     @Before
     fun setUp() {
-        utils = FooterActionsTestUtils(context, TestableLooper.get(this))
+        utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler)
+    }
+
+    private fun runTest(block: suspend TestScope.() -> Unit) {
+        testScope.runTest(testBody = block)
     }
 
     @Test
-    fun settingsButton() = runBlockingTest {
+    fun settingsButton() = runTest {
         val underTest = utils.footerActionsViewModel(showPowerButton = false)
         val settings = underTest.settings
 
@@ -87,7 +89,7 @@
     }
 
     @Test
-    fun powerButton() = runBlockingTest {
+    fun powerButton() = runTest {
         // Without power button.
         val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false)
         assertThat(underTestWithoutPower.power).isNull()
@@ -114,7 +116,7 @@
     }
 
     @Test
-    fun userSwitcher() = runBlockingTest {
+    fun userSwitcher() = runTest {
         val picture: Drawable = mock()
         val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
         val settings = FakeSettings()
@@ -135,7 +137,6 @@
                 showPowerButton = false,
                 footerActionsInteractor =
                     utils.footerActionsInteractor(
-                        bgDispatcher = testDispatcher,
                         userSwitcherRepository =
                             utils.userSwitcherRepository(
                                 userTracker = userTracker,
@@ -143,22 +144,12 @@
                                 userManager = userManager,
                                 userInfoController = userInfoController,
                                 userSwitcherController = userSwitcherControllerWrapper.controller,
-                                bgDispatcher = testDispatcher,
                             ),
                     )
             )
 
         // Collect the user switcher into currentUserSwitcher.
-        var currentUserSwitcher: FooterActionsButtonViewModel? = null
-        val job = launch { underTest.userSwitcher.collect { currentUserSwitcher = it } }
-        fun currentUserSwitcher(): FooterActionsButtonViewModel? {
-            // Make sure we finish collecting the current user switcher. This is necessary because
-            // combined flows launch multiple coroutines in the current scope so we need to make
-            // sure we process all coroutines triggered by our flow collection before we make
-            // assertions on the current buttons.
-            advanceUntilIdle()
-            return currentUserSwitcher
-        }
+        val currentUserSwitcher = collectLastValue(underTest.userSwitcher)
 
         // The user switcher is disabled.
         assertThat(currentUserSwitcher()).isNull()
@@ -203,12 +194,10 @@
         // in guest mode.
         userInfoController.updateInfo { this.picture = mock<UserIconDrawable>() }
         assertThat(iconTint()).isNull()
-
-        job.cancel()
     }
 
     @Test
-    fun security() = runBlockingTest {
+    fun security() = runTest {
         val securityController = FakeSecurityController()
         val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
 
@@ -224,22 +213,15 @@
                 footerActionsInteractor =
                     utils.footerActionsInteractor(
                         qsSecurityFooterUtils = qsSecurityFooterUtils,
-                        bgDispatcher = testDispatcher,
                         securityRepository =
                             utils.securityRepository(
                                 securityController = securityController,
-                                bgDispatcher = testDispatcher,
                             ),
                     ),
             )
 
         // Collect the security model into currentSecurity.
-        var currentSecurity: FooterActionsSecurityButtonViewModel? = null
-        val job = launch { underTest.security.collect { currentSecurity = it } }
-        fun currentSecurity(): FooterActionsSecurityButtonViewModel? {
-            advanceUntilIdle()
-            return currentSecurity
-        }
+        val currentSecurity = collectLastValue(underTest.security)
 
         // By default, we always return a null SecurityButtonConfig.
         assertThat(currentSecurity()).isNull()
@@ -270,12 +252,10 @@
         security = currentSecurity()
         assertThat(security).isNotNull()
         assertThat(security!!.onClick).isNull()
-
-        job.cancel()
     }
 
     @Test
-    fun foregroundServices() = runBlockingTest {
+    fun foregroundServices() = runTest {
         val securityController = FakeSecurityController()
         val fgsManagerController =
             FakeFgsManagerController(
@@ -300,21 +280,14 @@
                         securityRepository =
                             utils.securityRepository(
                                 securityController,
-                                bgDispatcher = testDispatcher,
                             ),
                         foregroundServicesRepository =
                             utils.foregroundServicesRepository(fgsManagerController),
-                        bgDispatcher = testDispatcher,
                     ),
             )
 
         // Collect the security model into currentSecurity.
-        var currentForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
-        val job = launch { underTest.foregroundServices.collect { currentForegroundServices = it } }
-        fun currentForegroundServices(): FooterActionsForegroundServicesButtonViewModel? {
-            advanceUntilIdle()
-            return currentForegroundServices
-        }
+        val currentForegroundServices = collectLastValue(underTest.foregroundServices)
 
         // We don't show the foreground services button if the number of running packages is not
         // > 1.
@@ -356,12 +329,10 @@
         }
         securityController.updateState {}
         assertThat(currentForegroundServices()?.displayText).isFalse()
-
-        job.cancel()
     }
 
     @Test
-    fun observeDeviceMonitoringDialogRequests() = runBlockingTest {
+    fun observeDeviceMonitoringDialogRequests() = runTest {
         val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
         val broadcastDispatcher = mock<BroadcastDispatcher>()
 
@@ -390,7 +361,6 @@
                     utils.footerActionsInteractor(
                         qsSecurityFooterUtils = qsSecurityFooterUtils,
                         broadcastDispatcher = broadcastDispatcher,
-                        bgDispatcher = testDispatcher,
                     ),
             )
 
@@ -415,7 +385,4 @@
         underTest.onVisibilityChangeRequested(visible = true)
         assertThat(underTest.isVisible.value).isTrue()
     }
-
-    private fun runBlockingTest(block: suspend TestScope.() -> Unit) =
-        runTest(testDispatcher) { block() }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 013e58e..69f3e987 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -33,6 +33,9 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,12 +52,16 @@
  */
 public class RecordingControllerTest extends SysuiTestCase {
 
+    private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+    private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
     @Mock
     private RecordingController.RecordingStateChangeCallback mCallback;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
     @Mock
     private UserContextProvider mUserContextProvider;
+    @Mock
+    private UserTracker mUserTracker;
 
     private RecordingController mController;
 
@@ -63,7 +70,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new RecordingController(mBroadcastDispatcher, mUserContextProvider);
+        mController = new RecordingController(mMainExecutor, mBroadcastDispatcher,
+                mUserContextProvider, mUserTracker);
         mController.addCallback(mCallback);
     }
 
@@ -176,9 +184,7 @@
         mController.updateState(true);
 
         // and user is changed
-        Intent intent = new Intent(Intent.ACTION_USER_SWITCHED)
-                .putExtra(Intent.EXTRA_USER_HANDLE, USER_ID);
-        mController.mUserChangeReceiver.onReceive(mContext, intent);
+        mController.mUserChangedCallback.onUserChanged(USER_ID, mContext);
 
         // Ensure that the recording was stopped
         verify(mCallback).onRecordingEnd();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 69a4559..b6f74f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -131,6 +131,7 @@
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -383,7 +384,8 @@
                                 mInteractionJankMonitor, mShadeExpansionStateManager),
                         mKeyguardBypassController,
                         mDozeParameters,
-                        mScreenOffAnimationController);
+                        mScreenOffAnimationController,
+                        mock(NotificationWakeUpCoordinatorLogger.class));
         mConfigurationController = new ConfigurationControllerImpl(mContext);
         PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
                 mContext,
@@ -499,8 +501,18 @@
                 mDumpManager);
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
+                null,
                 () -> {},
                 mNotificationShelfController);
+        mNotificationPanelViewController.setTrackingStartedListener(() -> {});
+        mNotificationPanelViewController.setOpenCloseListener(
+                new NotificationPanelViewController.OpenCloseListener() {
+                    @Override
+                    public void onClosingFinished() {}
+
+                    @Override
+                    public void onOpenStarted() {}
+                });
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
@@ -831,7 +843,7 @@
     public void handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
         when(mCommandQueue.panelsEnabled()).thenReturn(true);
         when(mView.isEnabled()).thenReturn(true);
-        MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+        MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0);
 
         mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
 
@@ -839,6 +851,17 @@
     }
 
     @Test
+    public void handleTouchEventFromStatusBar_topEdgeTouch_viewNeverReceivesEvent() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        when(mView.isEnabled()).thenReturn(true);
+        MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+
+        mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
+
+        verify(mView, never()).dispatchTouchEvent(event);
+    }
+
+    @Test
     public void testA11y_initializeNode() {
         AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
         mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 15a687d..452606d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static android.content.Intent.ACTION_USER_SWITCHED;
-
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
@@ -34,7 +32,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.os.Handler;
@@ -293,11 +290,9 @@
     }
 
     @Test
-    public void testActionUserSwitchedCallsOnUserSwitched() {
-        Intent intent = new Intent()
-                .setAction(ACTION_USER_SWITCHED)
-                .putExtra(Intent.EXTRA_USER_HANDLE, mSecondaryUser.id);
-        mLockscreenUserManager.getBaseBroadcastReceiverForTest().onReceive(mContext, intent);
+    public void testUserSwitchedCallsOnUserSwitched() {
+        mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanged(mSecondaryUser.id,
+                mContext);
         verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
     }
 
@@ -366,6 +361,10 @@
             return mBaseBroadcastReceiver;
         }
 
+        public UserTracker.Callback getUserTrackerCallbackForTest() {
+            return mUserChangedCallback;
+        }
+
         public ContentObserver getLockscreenSettingsObserverForTest() {
             return mLockscreenSettingsObserver;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 8b7b4de..6bd3f7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -26,22 +26,17 @@
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.collection.EntryUtilKt.modifyEntry;
-import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.argThat;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -54,10 +49,10 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -97,7 +92,7 @@
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private HighPriorityProvider mHighPriorityProvider;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
-    @Mock private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock private UserTracker mUserTracker;
     private final FakeSettings mFakeSettings = new FakeSettings();
 
     private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
@@ -117,7 +112,7 @@
                                 mKeyguardUpdateMonitor,
                                 mHighPriorityProvider,
                                 mStatusBarStateController,
-                                mBroadcastDispatcher,
+                                mUserTracker,
                                 mFakeSettings,
                                 mFakeSettings);
         mKeyguardNotificationVisibilityProvider = component.getProvider();
@@ -205,23 +200,19 @@
     }
 
     @Test
-    public void notifyListeners_onReceiveUserSwitchBroadcast() {
-        ArgumentCaptor<BroadcastReceiver> callbackCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mBroadcastDispatcher).registerReceiver(
+    public void notifyListeners_onReceiveUserSwitchCallback() {
+        ArgumentCaptor<UserTracker.Callback> callbackCaptor =
+                ArgumentCaptor.forClass(UserTracker.Callback.class);
+        verify(mUserTracker).addCallback(
                 callbackCaptor.capture(),
-                argThat(intentFilter -> intentFilter.hasAction(Intent.ACTION_USER_SWITCHED)),
-                isNull(),
-                isNull(),
-                eq(Context.RECEIVER_EXPORTED),
-                isNull());
-        BroadcastReceiver callback = callbackCaptor.getValue();
+                any());
+        UserTracker.Callback callback = callbackCaptor.getValue();
 
         Consumer<String> listener = mock(Consumer.class);
         mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
 
         when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
-        callback.onReceive(mContext, new Intent(Intent.ACTION_USER_SWITCHED));
+        callback.onUserChanged(CURR_USER_ID, mContext);
 
         verify(listener).accept(anyString());
     }
@@ -619,7 +610,7 @@
                     @BindsInstance KeyguardUpdateMonitor keyguardUpdateMonitor,
                     @BindsInstance HighPriorityProvider highPriorityProvider,
                     @BindsInstance SysuiStatusBarStateController statusBarStateController,
-                    @BindsInstance BroadcastDispatcher broadcastDispatcher,
+                    @BindsInstance UserTracker userTracker,
                     @BindsInstance SecureSettings secureSettings,
                     @BindsInstance GlobalSettings globalSettings
             );
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index ea311da..21aae00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -17,6 +17,7 @@
 
 
 import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.Notification.GROUP_ALERT_SUMMARY;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -33,6 +34,8 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -390,6 +393,127 @@
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
+    private long makeWhenHoursAgo(long hoursAgo) {
+        return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_flagDisabled() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(false);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        entry.getSbn().getNotification().when = makeWhenHoursAgo(25);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_whenNow() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_whenRecent() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        entry.getSbn().getNotification().when = makeWhenHoursAgo(13);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_whenZero() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        entry.getSbn().getNotification().when = 0L;
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(0L), anyLong(),
+                eq("when <= 0"));
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_whenNegative() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        entry.getSbn().getNotification().when = -1L;
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(-1L), anyLong(),
+                eq("when <= 0"));
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_hasFullScreenIntent() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+        long when = makeWhenHoursAgo(25);
+
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silent= */ false);
+        entry.getSbn().getNotification().when = when;
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
+                eq("full-screen intent"));
+    }
+
+    @Test
+    public void testShouldHeadsUp_oldWhen_isForegroundService() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+        long when = makeWhenHoursAgo(25);
+
+        NotificationEntry entry = createFgsNotification(IMPORTANCE_HIGH);
+        entry.getSbn().getNotification().when = when;
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
+                eq("foreground service"));
+    }
+
+    @Test
+    public void testShouldNotHeadsUp_oldWhen() throws Exception {
+        ensureStateForHeadsUpWhenAwake();
+        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+        long when = makeWhenHoursAgo(25);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        entry.getSbn().getNotification().when = when;
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+
+        verify(mLogger).logNoHeadsUpOldWhen(eq(entry), eq(when), anyLong());
+        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+    }
+
     @Test
     public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
         when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
@@ -763,6 +887,16 @@
         return createNotification(importance, n);
     }
 
+    private NotificationEntry createFgsNotification(int importance) {
+        Notification n = new Notification.Builder(getContext(), "a")
+                .setContentTitle("title")
+                .setContentText("content text")
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .build();
+
+        return createNotification(importance, n);
+    }
+
     private final NotificationInterruptSuppressor
             mSuppressAwakeHeadsUp =
             new NotificationInterruptSuppressor() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
new file mode 100644
index 0000000..2d23f3c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.SmartReplyConstants
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import junit.framework.Assert
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ExpandableNotificationRowControllerTest : SysuiTestCase() {
+
+    private val appName = "MyApp"
+    private val notifKey = "MyNotifKey"
+
+    private val view: ExpandableNotificationRow = mock()
+    private val activableNotificationViewController: ActivatableNotificationViewController = mock()
+    private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
+    private val metricsLogger: MetricsLogger = mock()
+    private val logBufferLogger: NotificationRowLogger = mock()
+    private val listContainer: NotificationListContainer = mock()
+    private val mediaManager: NotificationMediaManager = mock()
+    private val smartReplyConstants: SmartReplyConstants = mock()
+    private val smartReplyController: SmartReplyController = mock()
+    private val pluginManager: PluginManager = mock()
+    private val systemClock: SystemClock = mock()
+    private val keyguardBypassController: KeyguardBypassController = mock()
+    private val groupMembershipManager: GroupMembershipManager = mock()
+    private val groupExpansionManager: GroupExpansionManager = mock()
+    private val rowContentBindStage: RowContentBindStage = mock()
+    private val notifLogger: NotificationLogger = mock()
+    private val headsUpManager: HeadsUpManager = mock()
+    private val onExpandClickListener: ExpandableNotificationRow.OnExpandClickListener = mock()
+    private val statusBarStateController: StatusBarStateController = mock()
+    private val gutsManager: NotificationGutsManager = mock()
+    private val onUserInteractionCallback: OnUserInteractionCallback = mock()
+    private val falsingManager: FalsingManager = mock()
+    private val falsingCollector: FalsingCollector = mock()
+    private val featureFlags: FeatureFlags = mock()
+    private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
+    private val bubblesManager: BubblesManager = mock()
+    private val dragController: ExpandableNotificationRowDragController = mock()
+    private lateinit var controller: ExpandableNotificationRowController
+
+    @Before
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        controller =
+            ExpandableNotificationRowController(
+                view,
+                activableNotificationViewController,
+                rivSubComponentFactory,
+                metricsLogger,
+                logBufferLogger,
+                listContainer,
+                mediaManager,
+                smartReplyConstants,
+                smartReplyController,
+                pluginManager,
+                systemClock,
+                appName,
+                notifKey,
+                keyguardBypassController,
+                groupMembershipManager,
+                groupExpansionManager,
+                rowContentBindStage,
+                notifLogger,
+                headsUpManager,
+                onExpandClickListener,
+                statusBarStateController,
+                gutsManager,
+                /*allowLongPress=*/ false,
+                onUserInteractionCallback,
+                falsingManager,
+                falsingCollector,
+                featureFlags,
+                peopleNotificationIdentifier,
+                Optional.of(bubblesManager),
+                dragController
+            )
+    }
+
+    @After
+    fun tearDown() {
+        disallowTestableLooperAsMainThread()
+    }
+
+    @Test
+    fun offerKeepInParent_parentDismissed() {
+        whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+            .thenReturn(true)
+        whenever(view.isParentDismissed).thenReturn(true)
+
+        Assert.assertTrue(controller.offerToKeepInParentForAnimation())
+        Mockito.verify(view).setKeepInParentForDismissAnimation(true)
+    }
+
+    @Test
+    fun offerKeepInParent_parentNotDismissed() {
+        whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+            .thenReturn(true)
+
+        Assert.assertFalse(controller.offerToKeepInParentForAnimation())
+        Mockito.verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
+    }
+
+    @Test
+    fun removeFromParent_keptForAnimation() {
+        val parentView: ExpandableNotificationRow = mock()
+        whenever(view.notificationParent).thenReturn(parentView)
+        whenever(view.keepInParentForDismissAnimation()).thenReturn(true)
+
+        Assert.assertTrue(controller.removeFromParentIfKeptForAnimation())
+        Mockito.verify(parentView).removeChildNotification(view)
+    }
+
+    @Test
+    fun removeFromParent_notKeptForAnimation() {
+        val parentView: ExpandableNotificationRow = mock()
+        whenever(view.notificationParent).thenReturn(parentView)
+
+        Assert.assertFalse(controller.removeFromParentIfKeptForAnimation())
+        Mockito.verifyNoMoreInteractions(parentView)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 496bf37..088d165 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -299,7 +299,8 @@
     public ExpandableNotificationRow createBubble()
             throws Exception {
         Notification n = createNotification(false /* isGroupSummary */,
-                null /* groupKey */, makeBubbleMetadata(null));
+                null /* groupKey */,
+                makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
         n.flags |= FLAG_BUBBLE;
         ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
                 mDefaultInflationFlags, IMPORTANCE_HIGH);
@@ -332,7 +333,8 @@
     public ExpandableNotificationRow createBubbleInGroup()
             throws Exception {
         Notification n = createNotification(false /* isGroupSummary */,
-                GROUP_KEY /* groupKey */, makeBubbleMetadata(null));
+                GROUP_KEY /* groupKey */,
+                makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
         n.flags |= FLAG_BUBBLE;
         ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
                 mDefaultInflationFlags, IMPORTANCE_HIGH);
@@ -348,7 +350,7 @@
      * @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent}
      */
     public NotificationEntry createBubble(@Nullable PendingIntent deleteIntent) {
-        return createBubble(makeBubbleMetadata(deleteIntent), USER_HANDLE);
+        return createBubble(makeBubbleMetadata(deleteIntent, false /* autoExpand */), USER_HANDLE);
     }
 
     /**
@@ -357,7 +359,16 @@
      * @param handle the user to associate with this bubble.
      */
     public NotificationEntry createBubble(UserHandle handle) {
-        return createBubble(makeBubbleMetadata(null), handle);
+        return createBubble(makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */),
+                handle);
+    }
+
+    /**
+     * Returns an {@link NotificationEntry} that should be shown as a auto-expanded bubble.
+     */
+    public NotificationEntry createAutoExpandedBubble() {
+        return createBubble(makeBubbleMetadata(null /* deleteIntent */, true /* autoExpand */),
+                USER_HANDLE);
     }
 
     /**
@@ -565,7 +576,7 @@
         assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
     }
 
-    private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent) {
+    private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent, boolean autoExpand) {
         Intent target = new Intent(mContext, BubblesTestActivity.class);
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target,
                 PendingIntent.FLAG_MUTABLE);
@@ -574,6 +585,7 @@
                         Icon.createWithResource(mContext, R.drawable.android))
                 .setDeleteIntent(deleteIntent)
                 .setDesiredHeight(314)
+                .setAutoExpandBubble(autoExpand)
                 .build();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index ed84e42..521e518 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -106,6 +106,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.plugins.PluginDependencyProvider;
@@ -212,6 +213,7 @@
     @Mock private NotificationPanelView mNotificationPanelView;
     @Mock private IStatusBarService mBarService;
     @Mock private IDreamManager mDreamManager;
+    @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
     @Mock private ScrimController mScrimController;
     @Mock private DozeScrimController mDozeScrimController;
     @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -497,7 +499,8 @@
                 mDeviceStateManager,
                 mWiredChargingRippleController,
                 mDreamManager,
-                mCameraLauncherLazy) {
+                mCameraLauncherLazy,
+                () -> mLightRevealScrimViewModel) {
             @Override
             protected ViewRootImpl getViewRootImpl() {
                 return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index de71e2c..e475905 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1442,16 +1442,30 @@
 
     @Test
     public void testNotificationTransparency_followsTransitionToFullShade() {
+        mScrimController.setClipsQsScrim(true);
+
         mScrimController.transitionTo(SHADE_LOCKED);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
+
+        assertScrimTinted(Map.of(
+                mScrimInFront, false,
+                mScrimBehind, true,
+                mNotificationsScrim, false
+        ));
+
         float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
         mScrimController.transitionTo(ScrimState.KEYGUARD);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
         float keyguardAlpha = mNotificationsScrim.getViewAlpha();
 
-        mScrimController.setClipsQsScrim(true);
+        assertScrimTinted(Map.of(
+                mScrimInFront, true,
+                mScrimBehind, true,
+                mNotificationsScrim, true
+        ));
+
         float progress = 0.5f;
         float lsNotifProgress = 0.3f;
         mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
index b7a6c01..d35ce76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
@@ -22,7 +22,7 @@
 import android.provider.Settings.Global
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -45,7 +45,7 @@
 
     private lateinit var underTest: AirplaneModeRepositoryImpl
 
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: TableLogBuffer
     private lateinit var bgHandler: Handler
     private lateinit var scope: CoroutineScope
     private lateinit var settings: FakeSettings
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 76016a1..5a6bb30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -38,9 +38,9 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-class AirplaneModeViewModelTest : SysuiTestCase() {
+class AirplaneModeViewModelImplTest : SysuiTestCase() {
 
-    private lateinit var underTest: AirplaneModeViewModel
+    private lateinit var underTest: AirplaneModeViewModelImpl
 
     @Mock private lateinit var logger: ConnectivityPipelineLogger
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -57,7 +57,7 @@
         scope = CoroutineScope(IMMEDIATE)
 
         underTest =
-            AirplaneModeViewModel(
+            AirplaneModeViewModelImpl(
                 interactor,
                 logger,
                 scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 71b8bab..b38497a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -35,8 +35,9 @@
 import org.junit.Test
 
 @OptIn(ExperimentalCoroutinesApi::class)
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @SmallTest
-class WifiInteractorTest : SysuiTestCase() {
+class WifiInteractorImplTest : SysuiTestCase() {
 
     private lateinit var underTest: WifiInteractor
 
@@ -47,7 +48,7 @@
     fun setUp() {
         connectivityRepository = FakeConnectivityRepository()
         wifiRepository = FakeWifiRepository()
-        underTest = WifiInteractor(connectivityRepository, wifiRepository)
+        underTest = WifiInteractorImpl(connectivityRepository, wifiRepository)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 37457b3..5c16e129 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -32,12 +32,14 @@
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
@@ -86,9 +88,9 @@
         connectivityRepository = FakeConnectivityRepository()
         wifiRepository = FakeWifiRepository()
         wifiRepository.setIsWifiEnabled(true)
-        interactor = WifiInteractor(connectivityRepository, wifiRepository)
+        interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
         scope = CoroutineScope(Dispatchers.Unconfined)
-        airplaneModeViewModel = AirplaneModeViewModel(
+        airplaneModeViewModel = AirplaneModeViewModelImpl(
             AirplaneModeInteractor(
                 airplaneModeRepository,
                 connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index a1afcd7..3001b81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -37,6 +38,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
 import com.google.common.truth.Truth.assertThat
@@ -81,10 +83,10 @@
         connectivityRepository = FakeConnectivityRepository()
         wifiRepository = FakeWifiRepository()
         wifiRepository.setIsWifiEnabled(true)
-        interactor = WifiInteractor(connectivityRepository, wifiRepository)
+        interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
         scope = CoroutineScope(IMMEDIATE)
         airplaneModeViewModel =
-            AirplaneModeViewModel(
+            AirplaneModeViewModelImpl(
                 AirplaneModeInteractor(
                     airplaneModeRepository,
                     connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 7d2c560..6a6b2a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -30,6 +31,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import com.google.common.truth.Truth.assertThat
@@ -73,9 +75,9 @@
         connectivityRepository = FakeConnectivityRepository()
         wifiRepository = FakeWifiRepository()
         wifiRepository.setIsWifiEnabled(true)
-        interactor = WifiInteractor(connectivityRepository, wifiRepository)
+        interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
         scope = CoroutineScope(IMMEDIATE)
-        airplaneModeViewModel = AirplaneModeViewModel(
+        airplaneModeViewModel = AirplaneModeViewModelImpl(
             AirplaneModeInteractor(
                 airplaneModeRepository,
                 connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 2e527be1..034c618 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -95,6 +95,24 @@
     }
 
     @Test
+    fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() = runSelfCancelingTest {
+        underTest = create(this)
+
+        var value: UserSwitcherSettingsModel? = null
+        underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+
+        assertUserSwitcherSettings(
+            model = value,
+            expectedSimpleUserSwitcher = false,
+            expectedAddUsersFromLockscreen = false,
+            expectedUserSwitcherEnabled =
+                context.resources.getBoolean(
+                    com.android.internal.R.bool.config_showUserSwitcherByDefault
+                ),
+        )
+    }
+
+    @Test
     fun refreshUsers() = runSelfCancelingTest {
         underTest = create(this)
         val initialExpectedValue =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 50d239d..5beb2b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -761,7 +761,7 @@
         }
 
     @Test
-    fun `users - secondary user - no guest user`() =
+    fun `users - secondary user - guest user can be switched to`() =
         runBlocking(IMMEDIATE) {
             val userInfos = createUserInfos(count = 3, includeGuest = true)
             userRepository.setUserInfos(userInfos)
@@ -770,8 +770,8 @@
 
             var res: List<UserModel>? = null
             val job = underTest.users.onEach { res = it }.launchIn(this)
-            assertThat(res?.size == 2).isTrue()
-            assertThat(res?.find { it.isGuest }).isNull()
+            assertThat(res?.size == 3).isTrue()
+            assertThat(res?.find { it.isGuest }).isNotNull()
             job.cancel()
         }
 
@@ -813,7 +813,8 @@
             val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
 
             // Dialog is shown.
-            assertThat(dialogRequest).isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog)
+            assertThat(dialogRequest)
+                .isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog(expandable))
 
             underTest.onDialogShown()
             assertThat(dialogRequest).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 4b6bdac..784a26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -47,18 +47,14 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestResult
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -89,7 +85,6 @@
 
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var testScope: TestScope
-    private lateinit var injectedScope: CoroutineScope
 
     @Before
     fun setUp() {
@@ -104,7 +99,6 @@
 
         testDispatcher = UnconfinedTestDispatcher()
         testScope = TestScope(testDispatcher)
-        injectedScope = CoroutineScope(testScope.coroutineContext + SupervisorJob())
         userRepository = FakeUserRepository()
         runBlocking {
             userRepository.setSettings(
@@ -118,14 +112,14 @@
         powerRepository = FakePowerRepository()
         val refreshUsersScheduler =
             RefreshUsersScheduler(
-                applicationScope = injectedScope,
+                applicationScope = testScope.backgroundScope,
                 mainDispatcher = testDispatcher,
                 repository = userRepository,
             )
         val guestUserInteractor =
             GuestUserInteractor(
                 applicationContext = context,
-                applicationScope = injectedScope,
+                applicationScope = testScope.backgroundScope,
                 mainDispatcher = testDispatcher,
                 backgroundDispatcher = testDispatcher,
                 manager = manager,
@@ -154,7 +148,7 @@
                                     set(Flags.FULL_SCREEN_USER_SWITCHER, false)
                                 },
                             manager = manager,
-                            applicationScope = injectedScope,
+                            applicationScope = testScope.backgroundScope,
                             telephonyInteractor =
                                 TelephonyInteractor(
                                     repository = FakeTelephonyRepository(),
@@ -175,7 +169,7 @@
     }
 
     @Test
-    fun users() = selfCancelingTest {
+    fun users() = testScope.runTest {
         val userInfos =
             listOf(
                 UserInfo(
@@ -210,26 +204,26 @@
         assertUserViewModel(
             viewModel = userViewModels.last()[0],
             viewKey = 0,
-            name = "zero",
+            name = Text.Loaded("zero"),
             isSelectionMarkerVisible = true,
         )
         assertUserViewModel(
             viewModel = userViewModels.last()[1],
             viewKey = 1,
-            name = "one",
+            name = Text.Loaded("one"),
             isSelectionMarkerVisible = false,
         )
         assertUserViewModel(
             viewModel = userViewModels.last()[2],
             viewKey = 2,
-            name = "two",
+            name = Text.Loaded("two"),
             isSelectionMarkerVisible = false,
         )
         job.cancel()
     }
 
     @Test
-    fun `maximumUserColumns - few users`() = selfCancelingTest {
+    fun `maximumUserColumns - few users`() = testScope.runTest {
         setUsers(count = 2)
         val values = mutableListOf<Int>()
         val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -240,7 +234,7 @@
     }
 
     @Test
-    fun `maximumUserColumns - many users`() = selfCancelingTest {
+    fun `maximumUserColumns - many users`() = testScope.runTest {
         setUsers(count = 5)
         val values = mutableListOf<Int>()
         val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -250,7 +244,7 @@
     }
 
     @Test
-    fun `isOpenMenuButtonVisible - has actions - true`() = selfCancelingTest {
+    fun `isOpenMenuButtonVisible - has actions - true`() = testScope.runTest {
         setUsers(2)
 
         val isVisible = mutableListOf<Boolean>()
@@ -261,7 +255,7 @@
     }
 
     @Test
-    fun `isOpenMenuButtonVisible - no actions - false`() = selfCancelingTest {
+    fun `isOpenMenuButtonVisible - no actions - false`() = testScope.runTest {
         val userInfos = setUsers(2)
         userRepository.setSelectedUserInfo(userInfos[1])
         keyguardRepository.setKeyguardShowing(true)
@@ -275,7 +269,7 @@
     }
 
     @Test
-    fun menu() = selfCancelingTest {
+    fun menu() = testScope.runTest {
         val isMenuVisible = mutableListOf<Boolean>()
         val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) }
         assertThat(isMenuVisible.last()).isFalse()
@@ -290,7 +284,7 @@
     }
 
     @Test
-    fun `menu actions`() = selfCancelingTest {
+    fun `menu actions`() = testScope.runTest {
         setUsers(2)
         val actions = mutableListOf<List<UserActionViewModel>>()
         val job = launch(testDispatcher) { underTest.menu.toList(actions) }
@@ -309,7 +303,7 @@
     }
 
     @Test
-    fun `isFinishRequested - finishes when user is switched`() = selfCancelingTest {
+    fun `isFinishRequested - finishes when user is switched`() = testScope.runTest {
         val userInfos = setUsers(count = 2)
         val isFinishRequested = mutableListOf<Boolean>()
         val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
@@ -323,7 +317,7 @@
     }
 
     @Test
-    fun `isFinishRequested - finishes when the screen turns off`() = selfCancelingTest {
+    fun `isFinishRequested - finishes when the screen turns off`() = testScope.runTest {
         setUsers(count = 2)
         powerRepository.setInteractive(true)
         val isFinishRequested = mutableListOf<Boolean>()
@@ -338,7 +332,7 @@
     }
 
     @Test
-    fun `isFinishRequested - finishes when cancel button is clicked`() = selfCancelingTest {
+    fun `isFinishRequested - finishes when cancel button is clicked`() = testScope.runTest {
         setUsers(count = 2)
         powerRepository.setInteractive(true)
         val isFinishRequested = mutableListOf<Boolean>()
@@ -356,6 +350,93 @@
         job.cancel()
     }
 
+    @Test
+    fun `guest selected -- name is exit guest`() = testScope.runTest {
+        val userInfos =
+                listOf(
+                        UserInfo(
+                                /* id= */ 0,
+                                /* name= */ "zero",
+                                /* iconPath= */ "",
+                                /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+                                UserManager.USER_TYPE_FULL_SYSTEM,
+                        ),
+                        UserInfo(
+                                /* id= */ 1,
+                                /* name= */ "one",
+                                /* iconPath= */ "",
+                                /* flags= */ UserInfo.FLAG_FULL,
+                                UserManager.USER_TYPE_FULL_GUEST,
+                        ),
+                )
+
+        userRepository.setUserInfos(userInfos)
+        userRepository.setSelectedUserInfo(userInfos[1])
+
+        val userViewModels = mutableListOf<List<UserViewModel>>()
+        val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+        assertThat(userViewModels.last()).hasSize(2)
+        assertUserViewModel(
+                viewModel = userViewModels.last()[0],
+                viewKey = 0,
+                name = Text.Loaded("zero"),
+                isSelectionMarkerVisible = false,
+        )
+        assertUserViewModel(
+                viewModel = userViewModels.last()[1],
+                viewKey = 1,
+                name = Text.Resource(
+                    com.android.settingslib.R.string.guest_exit_quick_settings_button
+                ),
+                isSelectionMarkerVisible = true,
+        )
+        job.cancel()
+    }
+
+    @Test
+    fun `guest not selected -- name is guest`() = testScope.runTest {
+        val userInfos =
+                listOf(
+                        UserInfo(
+                                /* id= */ 0,
+                                /* name= */ "zero",
+                                /* iconPath= */ "",
+                                /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+                                UserManager.USER_TYPE_FULL_SYSTEM,
+                        ),
+                        UserInfo(
+                                /* id= */ 1,
+                                /* name= */ "one",
+                                /* iconPath= */ "",
+                                /* flags= */ UserInfo.FLAG_FULL,
+                                UserManager.USER_TYPE_FULL_GUEST,
+                        ),
+                )
+
+        userRepository.setUserInfos(userInfos)
+        userRepository.setSelectedUserInfo(userInfos[0])
+        runCurrent()
+
+        val userViewModels = mutableListOf<List<UserViewModel>>()
+        val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+        assertThat(userViewModels.last()).hasSize(2)
+        assertUserViewModel(
+                viewModel = userViewModels.last()[0],
+                viewKey = 0,
+                name = Text.Loaded("zero"),
+                isSelectionMarkerVisible = true,
+        )
+        assertUserViewModel(
+                viewModel = userViewModels.last()[1],
+                viewKey = 1,
+                name = Text.Loaded("one"),
+                isSelectionMarkerVisible = false,
+        )
+        job.cancel()
+    }
+
     private suspend fun setUsers(count: Int): List<UserInfo> {
         val userInfos =
             (0 until count).map { index ->
@@ -384,26 +465,18 @@
     private fun assertUserViewModel(
         viewModel: UserViewModel?,
         viewKey: Int,
-        name: String,
+        name: Text,
         isSelectionMarkerVisible: Boolean,
     ) {
         checkNotNull(viewModel)
         assertThat(viewModel.viewKey).isEqualTo(viewKey)
-        assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
+        assertThat(viewModel.name).isEqualTo(name)
         assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
         assertThat(viewModel.alpha)
             .isEqualTo(LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA)
         assertThat(viewModel.onClicked).isNotNull()
     }
 
-    private fun selfCancelingTest(
-        block: suspend TestScope.() -> Unit,
-    ): TestResult =
-        testScope.runTest {
-            block()
-            injectedScope.coroutineContext[Job.Key]?.cancelAndJoin()
-        }
-
     companion object {
         private const val SUPERVISED_USER_CREATION_PACKAGE = "com.some.package"
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 7df7077..6bfc2f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -51,15 +51,11 @@
             )
     }
 
-    @Test
-    fun notEnough() = runBlocking {
-        assertThatFlow(flowOf(1).pairwise()).emitsNothing()
-    }
+    @Test fun notEnough() = runBlocking { assertThatFlow(flowOf(1).pairwise()).emitsNothing() }
 
     @Test
     fun withInit() = runBlocking {
-        assertThatFlow(flowOf(2).pairwise(initialValue = 1))
-            .emitsExactly(WithPrev(1, 2))
+        assertThatFlow(flowOf(2).pairwise(initialValue = 1)).emitsExactly(WithPrev(1, 2))
     }
 
     @Test
@@ -68,25 +64,78 @@
     }
 
     @Test
-    fun withStateFlow() = runBlocking(Dispatchers.Main.immediate) {
-        val state = MutableStateFlow(1)
-        val stop = MutableSharedFlow<Unit>()
-
-        val stoppable = merge(state, stop)
-            .takeWhile { it is Int }
-            .filterIsInstance<Int>()
-
-        val job1 = launch {
-            assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2))
-        }
-        state.value = 2
-        val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
-
-        stop.emit(Unit)
-
-        assertThatJob(job1).isCompleted()
-        assertThatJob(job2).isCompleted()
+    fun withTransform() = runBlocking {
+        assertThatFlow(
+                flowOf("val1", "val2", "val3").pairwiseBy { prev: String, next: String ->
+                    "$prev|$next"
+                }
+            )
+            .emitsExactly("val1|val2", "val2|val3")
     }
+
+    @Test
+    fun withGetInit() = runBlocking {
+        var initRun = false
+        assertThatFlow(
+                flowOf("val1", "val2").pairwiseBy(
+                    getInitialValue = {
+                        initRun = true
+                        "initial"
+                    }
+                ) { prev: String, next: String -> "$prev|$next" }
+            )
+            .emitsExactly("initial|val1", "val1|val2")
+        assertThat(initRun).isTrue()
+    }
+
+    @Test
+    fun notEnoughWithGetInit() = runBlocking {
+        var initRun = false
+        assertThatFlow(
+                emptyFlow<String>().pairwiseBy(
+                    getInitialValue = {
+                        initRun = true
+                        "initial"
+                    }
+                ) { prev: String, next: String -> "$prev|$next" }
+            )
+            .emitsNothing()
+        // Even though the flow will not emit anything, the initial value function should still get
+        // run.
+        assertThat(initRun).isTrue()
+    }
+
+    @Test
+    fun getInitNotRunWhenFlowNotCollected() = runBlocking {
+        var initRun = false
+        flowOf("val1", "val2").pairwiseBy(
+            getInitialValue = {
+                initRun = true
+                "initial"
+            }
+        ) { prev: String, next: String -> "$prev|$next" }
+
+        // Since the flow isn't collected, ensure [initialValueFun] isn't run.
+        assertThat(initRun).isFalse()
+    }
+
+    @Test
+    fun withStateFlow() =
+        runBlocking(Dispatchers.Main.immediate) {
+            val state = MutableStateFlow(1)
+            val stop = MutableSharedFlow<Unit>()
+
+            val stoppable = merge(state, stop).takeWhile { it is Int }.filterIsInstance<Int>()
+
+            val job1 = launch { assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2)) }
+            state.value = 2
+            val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
+
+            stop.emit(Unit)
+
+            assertThatJob(job1).isCompleted()
+            assertThatJob(job2).isCompleted()
+        }
 }
 
 @SmallTest
@@ -94,18 +143,17 @@
 class SetChangesFlowTest : SysuiTestCase() {
     @Test
     fun simple() = runBlocking {
-        assertThatFlow(
-            flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges()
-        ).emitsExactly(
-            SetChanges(
-                added = setOf(1, 2, 3),
-                removed = emptySet(),
-            ),
-            SetChanges(
-                added = setOf(4),
-                removed = setOf(1),
-            ),
-        )
+        assertThatFlow(flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges())
+            .emitsExactly(
+                SetChanges(
+                    added = setOf(1, 2, 3),
+                    removed = emptySet(),
+                ),
+                SetChanges(
+                    added = setOf(4),
+                    removed = setOf(1),
+                ),
+            )
     }
 
     @Test
@@ -147,14 +195,19 @@
 class SampleFlowTest : SysuiTestCase() {
     @Test
     fun simple() = runBlocking {
-        assertThatFlow(flow { yield(); emit(1) }.sample(flowOf(2)) { a, b -> a to b })
+        assertThatFlow(
+                flow {
+                        yield()
+                        emit(1)
+                    }
+                    .sample(flowOf(2)) { a, b -> a to b }
+            )
             .emitsExactly(1 to 2)
     }
 
     @Test
     fun otherFlowNoValueYet() = runBlocking {
-        assertThatFlow(flowOf(1).sample(emptyFlow<Unit>()))
-            .emitsNothing()
+        assertThatFlow(flowOf(1).sample(emptyFlow<Unit>())).emitsNothing()
     }
 
     @Test
@@ -178,13 +231,14 @@
     }
 }
 
-private fun <T> assertThatFlow(flow: Flow<T>) = object {
-    suspend fun emitsExactly(vararg emissions: T) =
-        assertThat(flow.toList()).containsExactly(*emissions).inOrder()
-    suspend fun emitsNothing() =
-        assertThat(flow.toList()).isEmpty()
-}
+private fun <T> assertThatFlow(flow: Flow<T>) =
+    object {
+        suspend fun emitsExactly(vararg emissions: T) =
+            assertThat(flow.toList()).containsExactly(*emissions).inOrder()
+        suspend fun emitsNothing() = assertThat(flow.toList()).isEmpty()
+    }
 
-private fun assertThatJob(job: Job) = object {
-    fun isCompleted() = assertThat(job.isCompleted).isTrue()
-}
+private fun assertThatJob(job: Job) =
+    object {
+        fun isCompleted() = assertThat(job.isCompleted).isTrue()
+    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index db3b9b5..d31f0bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1423,13 +1423,43 @@
         assertStackCollapsed();
         // Post status bar state change update with the same value
         mBubbleController.onStatusBarStateChanged(false);
-        // Stack should remain collapsedb
+        // Stack should remain collapsed
         assertStackCollapsed();
         // Post status bar state change which should trigger bubble to expand
         mBubbleController.onStatusBarStateChanged(true);
         assertStackExpanded();
     }
 
+    /**
+     * Test to verify behavior for the following scenario:
+     * <ol>
+     *     <li>device is locked with keyguard on, status bar shade state updates to
+     *     <code>false</code></li>
+     *     <li>notification entry is marked to be a bubble and it is set to auto-expand</li>
+     *     <li>device unlock starts, status bar shade state receives another update to
+     *     <code>false</code></li>
+     *     <li>device is unlocked and status bar shade state is set to <code>true</code></li>
+     *     <li>bubble should be expanded</li>
+     * </ol>
+     */
+    @Test
+    public void testOnStatusBarStateChanged_newAutoExpandedBubbleRemainsExpanded() {
+        // Set device as locked
+        mBubbleController.onStatusBarStateChanged(false);
+
+        // Create a auto-expanded bubble
+        NotificationEntry entry = mNotificationTestHelper.createAutoExpandedBubble();
+        mEntryListener.onEntryAdded(entry);
+
+        // When unlocking, we may receive duplicate updates with shade=false, ensure they don't
+        // clear the expanded state
+        mBubbleController.onStatusBarStateChanged(false);
+        mBubbleController.onStatusBarStateChanged(true);
+
+        // After unlocking, stack should be expanded
+        assertStackExpanded();
+    }
+
     @Test
     public void testSetShouldAutoExpand_notifiesFlagChanged() {
         mBubbleController.updateBubble(mBubbleEntry);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index fa3cc99..bf2235a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -136,6 +136,8 @@
                 InstrumentationRegistry.getArguments());
         if (TestableLooper.get(this) != null) {
             TestableLooper.get(this).processAllMessages();
+            // Must remove static reference to this test object to prevent leak (b/261039202)
+            TestableLooper.remove(this);
         }
         disallowTestableLooperAsMainThread();
         mContext.cleanUpReceivers(this.getClass().getSimpleName());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
new file mode 100644
index 0000000..8176dd0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+
+class FakeOverlapDetector : OverlapDetector {
+    var shouldReturn: Boolean = false
+
+    override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
+        return shouldReturn
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 52e0c982..ad086ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -22,12 +22,12 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.UserHandle
-import android.util.ArraySet
 import android.util.Log
 import com.android.systemui.SysuiTestableContext
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.settings.UserTracker
+import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.Executor
 
 class FakeBroadcastDispatcher(
@@ -50,7 +50,7 @@
         PendingRemovalStore(logger)
     ) {
 
-    val registeredReceivers = ArraySet<BroadcastReceiver>()
+    val registeredReceivers: MutableSet<BroadcastReceiver> = ConcurrentHashMap.newKeySet()
 
     override fun registerReceiverWithHandler(
         receiver: BroadcastReceiver,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
new file mode 100644
index 0000000..b7a8d2e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.coroutines
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
+fun <T> TestScope.collectLastValue(
+    flow: Flow<T>,
+    context: CoroutineContext = EmptyCoroutineContext,
+    start: CoroutineStart = CoroutineStart.DEFAULT,
+): () -> T? {
+    var lastValue: T? = null
+    backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
+    return {
+        runCurrent()
+        lastValue
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
new file mode 100644
index 0000000..d85dd2e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+
+class FakeKeyguardQuickAffordanceProviderClientFactory(
+    private val userTracker: UserTracker,
+    private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
+        FakeKeyguardQuickAffordanceProviderClient()
+    },
+) : KeyguardQuickAffordanceProviderClientFactory {
+
+    override fun create(): KeyguardQuickAffordanceProviderClient {
+        return callback(userTracker.userId)
+    }
+}
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 3601667..5c2a915 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
@@ -17,11 +17,15 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.graphics.Point
 import com.android.systemui.common.shared.model.Position
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -49,7 +53,7 @@
     override val isDreaming: Flow<Boolean> = _isDreaming
 
     private val _dozeAmount = MutableStateFlow(0f)
-    override val dozeAmount: Flow<Float> = _dozeAmount
+    override val linearDozeAmount: Flow<Float> = _dozeAmount
 
     private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
     override val statusBarState: Flow<StatusBarState> = _statusBarState
@@ -57,8 +61,16 @@
     private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
     override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
 
-    private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
-    override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+    private val _wakefulnessModel =
+        MutableStateFlow(
+            WakefulnessModel(
+                WakefulnessState.ASLEEP,
+                false,
+                WakeSleepReason.OTHER,
+                WakeSleepReason.OTHER
+            )
+        )
+    override val wakefulness: Flow<WakefulnessModel> = _wakefulnessModel
 
     private val _isUdfpsSupported = MutableStateFlow(false)
 
@@ -71,6 +83,15 @@
     private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
     override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
 
+    private val _fingerprintSensorLocation = MutableStateFlow<Point?>(null)
+    override val fingerprintSensorLocation: Flow<Point?> = _fingerprintSensorLocation
+
+    private val _faceSensorLocation = MutableStateFlow<Point?>(null)
+    override val faceSensorLocation: Flow<Point?> = _faceSensorLocation
+
+    private val _biometricUnlockSource = MutableStateFlow<BiometricUnlockSource?>(null)
+    override val biometricUnlockSource: Flow<BiometricUnlockSource?> = _biometricUnlockSource
+
     override fun isKeyguardShowing(): Boolean {
         return _isKeyguardShowing.value
     }
@@ -99,6 +120,22 @@
         _dozeAmount.value = dozeAmount
     }
 
+    fun setBiometricUnlockState(state: BiometricUnlockModel) {
+        _biometricUnlockState.tryEmit(state)
+    }
+
+    fun setBiometricUnlockSource(source: BiometricUnlockSource?) {
+        _biometricUnlockSource.tryEmit(source)
+    }
+
+    fun setFaceSensorLocation(location: Point?) {
+        _faceSensorLocation.tryEmit(location)
+    }
+
+    fun setFingerprintSensorLocation(location: Point?) {
+        _fingerprintSensorLocation.tryEmit(location)
+    }
+
     override fun isUdfpsSupported(): Boolean {
         return _isUdfpsSupported.value
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
new file mode 100644
index 0000000..7c22604
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.statusbar.LightRevealEffect
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [LightRevealScrimRepository] */
+class FakeLightRevealScrimRepository : LightRevealScrimRepository {
+
+    private val _revealEffect: MutableStateFlow<LightRevealEffect> =
+        MutableStateFlow(DEFAULT_REVEAL_EFFECT)
+    override val revealEffect = _revealEffect
+
+    fun setRevealEffect(effect: LightRevealEffect) {
+        _revealEffect.tryEmit(effect)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 63448e2..1a893f8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -56,7 +56,8 @@
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.GlobalSettings
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
 
 /**
  * Util class to create real implementations of the FooterActions repositories, viewModel and
@@ -65,6 +66,7 @@
 class FooterActionsTestUtils(
     private val context: Context,
     private val testableLooper: TestableLooper,
+    private val scheduler: TestCoroutineScheduler,
 ) {
     /** Enable or disable the user switcher in the settings. */
     fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) {
@@ -105,7 +107,7 @@
         foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
         userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
         broadcastDispatcher: BroadcastDispatcher = mock(),
-        bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+        bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
     ): FooterActionsInteractor {
         return FooterActionsInteractorImpl(
             activityStarter,
@@ -126,7 +128,7 @@
     /** Create a [SecurityRepository] to be used in tests. */
     fun securityRepository(
         securityController: SecurityController = FakeSecurityController(),
-        bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+        bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
     ): SecurityRepository {
         return SecurityRepositoryImpl(
             securityController,
@@ -145,7 +147,7 @@
     fun userSwitcherRepository(
         @Application context: Context = this.context.applicationContext,
         bgHandler: Handler = Handler(testableLooper.looper),
-        bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+        bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
         userManager: UserManager = mock(),
         userTracker: UserTracker = FakeUserTracker(),
         userSwitcherController: UserSwitcherController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index a7eadba..0dd1fc7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -66,7 +66,8 @@
         _userId = _userInfo.id
         _userHandle = UserHandle.of(_userId)
 
-        callbacks.forEach { it.onUserChanged(_userId, userContext) }
+        val copy = callbacks.toList()
+        copy.forEach { it.onUserChanged(_userId, userContext) }
     }
 
     fun onProfileChanged() {
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 2814196..f1ba5ff 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -355,10 +355,10 @@
 
     private boolean activityMatchesDisplayCategory(ActivityInfo activityInfo) {
         if (mDisplayCategories.isEmpty()) {
-            return activityInfo.targetDisplayCategory == null;
+            return activityInfo.requiredDisplayCategory == null;
         }
-        return activityInfo.targetDisplayCategory != null
-                    && mDisplayCategories.contains(activityInfo.targetDisplayCategory);
+        return activityInfo.requiredDisplayCategory != null
+                    && mDisplayCategories.contains(activityInfo.requiredDisplayCategory);
 
     }
 
@@ -375,9 +375,9 @@
         }
         if (!activityMatchesDisplayCategory(activityInfo)) {
             Slog.d(TAG, String.format(
-                    "The activity's target display category: %s is not found on virtual display"
-                            + " with the following allowed display categories: %s",
-                    activityInfo.targetDisplayCategory, mDisplayCategories.toString()));
+                    "The activity's required display category: %s is not found on virtual display"
+                            + " with the following categories: %s",
+                    activityInfo.requiredDisplayCategory, mDisplayCategories.toString()));
             return false;
         }
         final UserHandle activityUser =
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 59024e7..6cd7ce8 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -310,27 +310,27 @@
                 Bundle packageMeasurement = measurePackage(packageInfo);
                 results.add(packageMeasurement);
 
-                if (record) {
+                if (record && (mba_status == MBA_STATUS_UPDATED_PRELOAD)) {
                     // compute digests of signing info
                     String[] signerDigestHexStrings = computePackageSignerSha256Digests(
                             packageInfo.signingInfo);
 
                     // now we should have all the bits for the atom
-                    /*  TODO: Uncomment and test after merging new atom definition.
+                    byte[] cDigest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
                     FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
                             packageInfo.packageName,
                             packageInfo.getLongVersionCode(),
-                            HexEncoding.encodeToString(packageMeasurement.getByteArray(
-                                    BUNDLE_CONTENT_DIGEST), false),
+                            (cDigest != null) ? HexEncoding.encodeToString(
+                                    packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST),
+                                    false) : null,
                             packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
                             signerDigestHexStrings, // signer_cert_digest
-                            mba_status,                 // mba_status
+                            mba_status,             // mba_status
                             null,                   // initiator
                             null,                   // initiator_signer_digest
                             null,                   // installer
                             null                    // originator
                     );
-                     */
                 }
             }
             if (DEBUG) {
@@ -377,12 +377,13 @@
                     }
 
                     // we should now have all the info needed for the atom
-                    /*  TODO: Uncomment and test after merging new atom definition.
+                    byte[] cDigest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
                     FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
                             packageInfo.packageName,
                             packageInfo.getLongVersionCode(),
-                            HexEncoding.encodeToString(packageMeasurement.getByteArray(
-                                    BUNDLE_CONTENT_DIGEST), false),
+                            (cDigest != null) ? HexEncoding.encodeToString(
+                                    packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST),
+                                    false) : null,
                             packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM),
                             signerDigestHexStrings,
                             MBA_STATUS_NEW_INSTALL,   // mba_status
@@ -391,7 +392,6 @@
                             installer,
                             originator
                     );
-                     */
                 }
             }
             if (DEBUG) {
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 551ffff..84c033c 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -341,7 +341,8 @@
         // non-proto tombstones, even though proto tombstones do not support including the counter
         // of events dropped since rate limiting activated yet.
         DropboxRateLimiter.RateLimitResult rateLimitResult =
-                sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE, processName);
+                sDropboxRateLimiter.shouldRateLimit(
+                       proto ? TAG_TOMBSTONE_PROTO : TAG_TOMBSTONE, processName);
         if (rateLimitResult.shouldRateLimit()) return;
 
         HashMap<String, Long> timestamps = readTimestamps();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50be458..c51e14f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5149,8 +5149,10 @@
                     ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
                     ApplicationExitInfo.SUBREASON_UNKNOWN,
                     "wrong startSeq");
-            app.killLocked("unexpected process record",
-                    ApplicationExitInfo.REASON_OTHER, true);
+            synchronized (this) {
+                app.killLocked("unexpected process record",
+                        ApplicationExitInfo.REASON_OTHER, true);
+            }
             return;
         }
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4d86140..af0bd63 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -100,7 +100,6 @@
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -177,7 +176,8 @@
     static final int START_USER_SWITCH_FG_MSG = 120;
     static final int COMPLETE_USER_SWITCH_MSG = 130;
     static final int USER_COMPLETED_EVENT_MSG = 140;
-    static final int USER_VISIBILITY_CHANGED_MSG = 150;
+
+    private static final int NO_ARG2 = 0;
 
     // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
     // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
@@ -437,20 +437,6 @@
     /** @see #getLastUserUnlockingUptime */
     private volatile long mLastUserUnlockingUptime = 0;
 
-    // TODO(b/244333150) remove this array and let UserVisibilityMediator call the listeners
-    // directly, as that class should be responsible for all user visibility logic (for example,
-    // when the foreground user is switched out, its profiles also become invisible)
-    /**
-     * List of visible users (as defined by {@link UserManager#isUserVisible()}).
-     *
-     * <p>It's only used to call {@link UserManagerInternal} when the visibility is changed upon
-     * the user starting or stopping.
-     *
-     * <p>Note: only the key is used, not the value.
-     */
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mVisibleUsers = new SparseBooleanArray();
-
     private final UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
         @Override
         public void onUserCreated(UserInfo user, Object token) {
@@ -1092,24 +1078,11 @@
             // instead.
             userManagerInternal.unassignUserFromDisplayOnStop(userId);
 
-            final boolean visibilityChanged;
-            boolean visibleBefore;
-            synchronized (mLock) {
-                visibleBefore = mVisibleUsers.get(userId);
-                if (visibleBefore) {
-                    deleteVisibleUserLocked(userId);
-                    visibilityChanged = true;
-                } else {
-                    visibilityChanged = false;
-                }
-            }
-
             updateStartedUserArrayLU();
 
             final boolean allowDelayedLockingCopied = allowDelayedLocking;
             Runnable finishUserStoppingAsync = () ->
-                    mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied,
-                            visibilityChanged));
+                    mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied));
 
             if (mInjector.getUserManager().isPreCreated(userId)) {
                 finishUserStoppingAsync.run();
@@ -1146,22 +1119,8 @@
         }
     }
 
-    private void addVisibleUserLocked(@UserIdInt int userId) {
-        if (DEBUG_MU) {
-            Slogf.d(TAG, "adding %d to mVisibleUsers", userId);
-        }
-        mVisibleUsers.put(userId, true);
-    }
-
-    private void deleteVisibleUserLocked(@UserIdInt int userId) {
-        if (DEBUG_MU) {
-            Slogf.d(TAG, "deleting %d from mVisibleUsers", userId);
-        }
-        mVisibleUsers.delete(userId);
-    }
-
     private void finishUserStopping(final int userId, final UserState uss,
-            final boolean allowDelayedLocking, final boolean visibilityChanged) {
+            final boolean allowDelayedLocking) {
         EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
         synchronized (mLock) {
             if (uss.state != UserState.STATE_STOPPING) {
@@ -1179,9 +1138,6 @@
                 BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
                 Integer.toString(userId), userId);
         mInjector.getSystemServiceManager().onUserStopping(userId);
-        if (visibilityChanged) {
-            mInjector.onUserVisibilityChanged(userId, /* visible= */ false);
-        }
 
         Runnable finishUserStoppedAsync = () ->
                 mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
@@ -1655,25 +1611,13 @@
                     userInfo.profileGroupId, foreground, displayId);
             t.traceEnd();
 
-            boolean visible;
-            switch (result) {
-                case UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE:
-                    visible = true;
-                    break;
-                case UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE:
-                    visible = false;
-                    break;
-                default:
-                    Slogf.wtf(TAG, "Wrong result from assignUserToDisplayOnStart(): %d", result);
-                    // Fall through
-                case UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE:
-                    Slogf.e(TAG, "%s user(%d) / display (%d) assignment failed: %s",
-                            (foreground ? "fg" : "bg"), userId, displayId,
-                            UserManagerInternal.userAssignmentResultToString(result));
-                    return false;
+            if (result == UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE) {
+                Slogf.e(TAG, "%s user(%d) / display (%d) assignment failed: %s",
+                        (foreground ? "fg" : "bg"), userId, displayId,
+                        UserManagerInternal.userAssignmentResultToString(result));
+                return false;
             }
 
-
             // TODO(b/239982558): might need something similar for bg users on secondary display
             if (foreground && isUserSwitchUiEnabled()) {
                 t.traceBegin("startFreezingScreen");
@@ -1724,15 +1668,7 @@
                 // Make sure the old user is no longer considering the display to be on.
                 mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
                 boolean userSwitchUiEnabled;
-                // TODO(b/244333150): temporary state until the callback logic is moved to
-                // UserVisibilityManager
-                int previousCurrentUserId; boolean notifyPreviousCurrentUserId;
                 synchronized (mLock) {
-                    previousCurrentUserId = mCurrentUserId;
-                    notifyPreviousCurrentUserId = mVisibleUsers.get(previousCurrentUserId);
-                    if (notifyPreviousCurrentUserId) {
-                        deleteVisibleUserLocked(previousCurrentUserId);
-                    }
                     mCurrentUserId = userId;
                     mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
                     userSwitchUiEnabled = mUserSwitchUiEnabled;
@@ -1753,10 +1689,6 @@
                         mInjector.getWindowManager().lockNow(null);
                     }
                 }
-                if (notifyPreviousCurrentUserId) {
-                    mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG,
-                            previousCurrentUserId, 0));
-                }
 
             } else {
                 final Integer currentUserIdInt = mCurrentUserId;
@@ -1768,12 +1700,6 @@
             }
             t.traceEnd();
 
-            if (visible) {
-                synchronized (mLock) {
-                    addVisibleUserLocked(userId);
-                }
-            }
-
             // Make sure user is in the started state.  If it is currently
             // stopping, we need to knock that off.
             if (uss.state == UserState.STATE_STOPPING) {
@@ -1810,20 +1736,10 @@
                 // Booting up a new user, need to tell system services about it.
                 // Note that this is on the same handler as scheduling of broadcasts,
                 // which is important because it needs to go first.
-                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId,
-                        visible ? 1 : 0));
+                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, NO_ARG2));
                 t.traceEnd();
             }
 
-            if (visible) {
-                // User was already running and became visible (for example, when switching to a
-                // user that was started in the background before), so it's necessary to explicitly
-                // notify the services (while when the user starts from BOOTING, USER_START_MSG
-                // takes care of that.
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(USER_VISIBILITY_CHANGED_MSG, userId, 1));
-            }
-
             t.traceBegin("sendMessages");
             if (foreground) {
                 mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
@@ -1853,7 +1769,7 @@
 
             if (foreground) {
                 t.traceBegin("moveUserToForeground");
-                moveUserToForeground(uss, oldUserId, userId);
+                moveUserToForeground(uss, userId);
                 t.traceEnd();
             } else {
                 t.traceBegin("finishUserBoot");
@@ -2067,21 +1983,25 @@
 
     /** Called on handler thread */
     @VisibleForTesting
-    void dispatchUserSwitchComplete(@UserIdInt int userId) {
+    void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
-        t.traceBegin("dispatchUserSwitchComplete-" + userId);
+        t.traceBegin("dispatchUserSwitchComplete-" + newUserId);
         mInjector.getWindowManager().setSwitchingUser(false);
         final int observerCount = mUserSwitchObservers.beginBroadcast();
         for (int i = 0; i < observerCount; i++) {
             try {
-                t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " "
+                t.traceBegin("onUserSwitchComplete-" + newUserId + " #" + i + " "
                         + mUserSwitchObservers.getBroadcastCookie(i));
-                mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+                mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId);
                 t.traceEnd();
             } catch (RemoteException e) {
+                // Ignore
             }
         }
         mUserSwitchObservers.finishBroadcast();
+        t.traceBegin("sendUserSwitchBroadcasts-" + oldUserId + "-" + newUserId);
+        sendUserSwitchBroadcasts(oldUserId, newUserId);
+        t.traceEnd();
         t.traceEnd();
     }
 
@@ -2243,22 +2163,18 @@
 
         // Do the keyguard dismiss and unfreeze later
         mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG);
-        mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0));
+        mHandler.sendMessage(mHandler.obtainMessage(
+                COMPLETE_USER_SWITCH_MSG, oldUserId, newUserId));
 
         uss.switching = false;
         stopGuestOrEphemeralUserIfBackground(oldUserId);
         stopUserOnSwitchIfEnforced(oldUserId);
-        if (oldUserId == UserHandle.USER_SYSTEM) {
-            // System user is never stopped, but its visibility is changed (as it is brought to the
-            // background)
-            updateSystemUserVisibility(t, /* visible= */ false);
-        }
 
         t.traceEnd(); // end continueUserSwitch
     }
 
     @VisibleForTesting
-    void completeUserSwitch(int newUserId) {
+    void completeUserSwitch(int oldUserId, int newUserId) {
         final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled();
         final Runnable runnable = () -> {
             if (isUserSwitchUiEnabled) {
@@ -2266,7 +2182,7 @@
             }
             mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
             mHandler.sendMessage(mHandler.obtainMessage(
-                    REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
+                    REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
         };
 
         // If there is no challenge set, dismiss the keyguard right away
@@ -2291,7 +2207,7 @@
         t.traceEnd();
     }
 
-    private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
+    private void moveUserToForeground(UserState uss, int newUserId) {
         boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
         if (homeInFront) {
             mInjector.startHomeActivity(newUserId, "moveUserToForeground");
@@ -2299,7 +2215,6 @@
             mInjector.taskSupervisorResumeFocusedStackTopActivity();
         }
         EventLogTags.writeAmSwitchUser(newUserId);
-        sendUserSwitchBroadcasts(oldUserId, newUserId);
     }
 
     void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
@@ -2614,27 +2529,10 @@
             // Don't need to call on HSUM because it will be called when the system user is
             // restarted on background
             mInjector.onUserStarting(UserHandle.USER_SYSTEM);
-            mInjector.onUserVisibilityChanged(UserHandle.USER_SYSTEM, /* visible= */ true);
+            mInjector.onSystemUserVisibilityChanged(/* visible= */ true);
         }
     }
 
-    private void updateSystemUserVisibility(TimingsTraceAndSlog t, boolean visible) {
-        t.traceBegin("update-system-userVisibility-" + visible);
-        if (DEBUG_MU) {
-            Slogf.d(TAG, "updateSystemUserVisibility(): visible=%b", visible);
-        }
-        int userId = UserHandle.USER_SYSTEM;
-        synchronized (mLock) {
-            if (visible) {
-                addVisibleUserLocked(userId);
-            } else {
-                deleteVisibleUserLocked(userId);
-            }
-        }
-        mInjector.onUserVisibilityChanged(userId, visible);
-        t.traceEnd();
-    }
-
     /**
      * Refreshes the internal caches related to user profiles.
      *
@@ -3032,9 +2930,6 @@
                     proto.end(uToken);
                 }
             }
-            for (int i = 0; i < mVisibleUsers.size(); i++) {
-                proto.write(UserControllerProto.VISIBLE_USERS_ARRAY, mVisibleUsers.keyAt(i));
-            }
             proto.write(UserControllerProto.CURRENT_USER, mCurrentUserId);
             for (int i = 0; i < mCurrentProfileIds.length; i++) {
                 proto.write(UserControllerProto.CURRENT_PROFILES, mCurrentProfileIds[i]);
@@ -3094,7 +2989,6 @@
                 pw.println("  mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
             }
             pw.println("  mLastUserUnlockingUptime: " + mLastUserUnlockingUptime);
-            pw.println("  mVisibleUsers: " + mVisibleUsers);
         }
     }
 
@@ -3190,9 +3084,8 @@
                 dispatchForegroundProfileChanged(msg.arg1);
                 break;
             case REPORT_USER_SWITCH_COMPLETE_MSG:
-                dispatchUserSwitchComplete(msg.arg1);
-
-                logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER,
+                dispatchUserSwitchComplete(msg.arg1, msg.arg2);
+                logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER,
                         USER_LIFECYCLE_EVENT_STATE_FINISH);
                 break;
             case REPORT_LOCKED_BOOT_COMPLETE_MSG:
@@ -3210,11 +3103,7 @@
                 logAndClearSessionId(msg.arg1);
                 break;
             case COMPLETE_USER_SWITCH_MSG:
-                completeUserSwitch(msg.arg1);
-                break;
-            case USER_VISIBILITY_CHANGED_MSG:
-                mInjector.onUserVisibilityChanged(/* userId= */ msg.arg1,
-                        /* visible= */ msg.arg2 == 1);
+                completeUserSwitch(msg.arg1, msg.arg2);
                 break;
         }
         return false;
@@ -3750,8 +3639,8 @@
             getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
         }
 
-        void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
-            getUserManagerInternal().onUserVisibilityChanged(userId, visible);
+        void onSystemUserVisibilityChanged(boolean visible) {
+            getUserManagerInternal().onSystemUserVisibilityChanged(visible);
         }
     }
 }
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index a954164..cda18b0 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -40,6 +40,7 @@
 import android.app.GameState;
 import android.app.IGameManagerService;
 import android.app.IGameModeListener;
+import android.app.StatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -76,6 +77,7 @@
 import android.util.AttributeSet;
 import android.util.KeyValueListParser;
 import android.util.Slog;
+import android.util.StatsEvent;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -104,6 +106,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Service to manage game related features.
@@ -115,6 +118,14 @@
  */
 public final class GameManagerService extends IGameManagerService.Stub {
     public static final String TAG = "GameManagerService";
+    // event strings used for logging
+    private static final String EVENT_SET_GAME_MODE = "SET_GAME_MODE";
+    private static final String EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG =
+            "UPDATE_CUSTOM_GAME_MODE_CONFIG";
+    private static final String EVENT_RECEIVE_SHUTDOWN_INDENT = "RECEIVE_SHUTDOWN_INDENT";
+    private static final String EVENT_ON_USER_STARTING = "ON_USER_STARTING";
+    private static final String EVENT_ON_USER_SWITCHING = "ON_USER_SWITCHING";
+    private static final String EVENT_ON_USER_STOPPING = "ON_USER_STOPPING";
 
     private static final boolean DEBUG = false;
 
@@ -449,6 +460,7 @@
     public void setGameState(String packageName, @NonNull GameState gameState,
             @UserIdInt int userId) {
         if (!isPackageGame(packageName, userId)) {
+            Slog.d(TAG, "No-op for attempt to set game state for non-game app: " + packageName);
             // Restrict to games only.
             return;
         }
@@ -932,6 +944,7 @@
         public void onBootPhase(int phase) {
             if (phase == PHASE_BOOT_COMPLETED) {
                 mService.onBootCompleted();
+                mService.registerStatsCallbacks();
             }
         }
 
@@ -975,11 +988,8 @@
         }
     }
 
-    private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
-        final GamePackageConfiguration config;
-        synchronized (mDeviceConfigLock) {
-            config = mConfigs.get(packageName);
-        }
+    private @GameMode int[] getAvailableGameModesUnchecked(String packageName, int userId) {
+        final GamePackageConfiguration config = getConfig(packageName, userId);
         if (config == null) {
             return new int[]{GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM};
         }
@@ -1002,9 +1012,13 @@
      */
     @Override
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
-    public @GameMode int[] getAvailableGameModes(String packageName) throws SecurityException {
+    public @GameMode int[] getAvailableGameModes(String packageName, int userId)
+            throws SecurityException {
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
-        return getAvailableGameModesUnchecked(packageName);
+        if (!isPackageGame(packageName, userId)) {
+            return new int[]{};
+        }
+        return getAvailableGameModesUnchecked(packageName, userId);
     }
 
     private @GameMode int getGameModeFromSettingsUnchecked(String packageName,
@@ -1071,7 +1085,6 @@
         // Check the caller has the necessary permission.
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
 
-        // Restrict to games only.
         if (!isPackageGame(packageName, userId)) {
             return null;
         }
@@ -1101,7 +1114,7 @@
         } else {
             return new GameModeInfo.Builder()
                     .setActiveGameMode(activeGameMode)
-                    .setAvailableGameModes(getAvailableGameModesUnchecked(packageName))
+                    .setAvailableGameModes(getAvailableGameModesUnchecked(packageName, userId))
                     .build();
         }
     }
@@ -1115,9 +1128,11 @@
     public void setGameMode(String packageName, @GameMode int gameMode, int userId)
             throws SecurityException {
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
-
-        if (!isPackageGame(packageName, userId) || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
-            // Restrict to games and valid game modes only.
+        if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+            Slog.d(TAG, "No-op for attempt to set UNSUPPORTED mode for app: " + packageName);
+            return;
+        } else if (!isPackageGame(packageName, userId)) {
+            Slog.d(TAG, "No-op for attempt to set game mode for non-game app: " + packageName);
             return;
         }
         int fromGameMode;
@@ -1147,9 +1162,18 @@
                 }
             }
         }
-        sendUserMessage(userId, WRITE_SETTINGS, "SET_GAME_MODE", WRITE_DELAY_MILLIS);
+        sendUserMessage(userId, WRITE_SETTINGS, EVENT_SET_GAME_MODE, WRITE_DELAY_MILLIS);
         sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
-                "SET_GAME_MODE", 0 /*delayMillis*/);
+                EVENT_SET_GAME_MODE, 0 /*delayMillis*/);
+        int gameUid = -1;
+        try {
+            gameUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+        } catch (NameNotFoundException ex) {
+            Slog.d(TAG, "Cannot find the UID for package " + packageName + " under user " + userId);
+        }
+        FrameworkStatsLog.write(FrameworkStatsLog.GAME_MODE_CHANGED, gameUid,
+                Binder.getCallingUid(), gameModeToStatsdGameMode(fromGameMode),
+                gameModeToStatsdGameMode(gameMode));
     }
 
     /**
@@ -1216,17 +1240,16 @@
                 Binder.getCallingUid(), userId, false, true, "notifyGraphicsEnvironmentSetup",
                 "com.android.server.app.GameManagerService");
 
-        // Restrict to games only.
-        if (!isPackageGame(packageName, userId)) {
-            return;
-        }
-
         if (!isValidPackageName(packageName, userId)) {
+            Slog.d(TAG, "No-op for attempt to notify graphics env setup for different package"
+                    + "than caller with uid: " + Binder.getCallingUid());
             return;
         }
 
         final int gameMode = getGameMode(packageName, userId);
         if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+            Slog.d(TAG, "No-op for attempt to notify graphics env setup for non-game app: "
+                    + packageName);
             return;
         }
         int loadingBoostDuration = getLoadingBoostDuration(packageName, userId);
@@ -1341,6 +1364,11 @@
             GameModeConfiguration gameModeConfig, int userId)
             throws SecurityException, IllegalArgumentException {
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        if (!isPackageGame(packageName, userId)) {
+            Slog.d(TAG, "No-op for attempt to update custom game mode for non-game app: "
+                    + packageName);
+            return;
+        }
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
                 throw new IllegalArgumentException("User " + userId + " wasn't started");
@@ -1364,11 +1392,26 @@
         }
         GamePackageConfiguration.GameModeConfiguration internalConfig =
                 configOverride.getOrAddDefaultGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+        int gameUid = -1;
+        try {
+            gameUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+        } catch (NameNotFoundException ex) {
+            Slog.d(TAG, "Cannot find the UID for package " + packageName + " under user " + userId);
+        }
+        FrameworkStatsLog.write(FrameworkStatsLog.GAME_MODE_CONFIGURATION_CHANGED, gameUid,
+                Binder.getCallingUid(), gameModeToStatsdGameMode(GameManager.GAME_MODE_CUSTOM),
+                internalConfig.getScaling(), gameModeConfig.getScalingFactor(),
+                internalConfig.getFps(), gameModeConfig.getFpsOverride());
         internalConfig.updateFromPublicGameModeConfig(gameModeConfig);
 
         Slog.i(TAG, "Updated custom game mode config for package: " + packageName
                 + " with FPS=" + internalConfig.getFps() + ";Scaling="
-                + internalConfig.getScaling());
+                + internalConfig.getScaling() + " under user " + userId);
+
+        sendUserMessage(userId, WRITE_SETTINGS, EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG,
+                WRITE_DELAY_MILLIS);
+        sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
+                EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG, WRITE_DELAY_MILLIS /*delayMillis*/);
     }
 
     /**
@@ -1443,9 +1486,10 @@
                         for (Map.Entry<Integer, GameManagerSettings> entry : mSettings.entrySet()) {
                             final int userId = entry.getKey();
                             sendUserMessage(userId, WRITE_SETTINGS,
-                                    Intent.ACTION_SHUTDOWN, 0 /*delayMillis*/);
+                                    EVENT_RECEIVE_SHUTDOWN_INDENT, 0 /*delayMillis*/);
                             sendUserMessage(userId,
-                                    WRITE_GAME_MODE_INTERVENTION_LIST_FILE, Intent.ACTION_SHUTDOWN,
+                                    WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
+                                    EVENT_RECEIVE_SHUTDOWN_INDENT,
                                     0 /*delayMillis*/);
                         }
                     }
@@ -1470,7 +1514,8 @@
                 userSettings.readPersistentDataLocked();
             }
         }
-        sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_STARTING", 0 /*delayMillis*/);
+        sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, EVENT_ON_USER_STARTING,
+                0 /*delayMillis*/);
 
         if (mGameServiceController != null) {
             mGameServiceController.notifyUserStarted(user);
@@ -1490,7 +1535,7 @@
             if (!mSettings.containsKey(userId)) {
                 return;
             }
-            sendUserMessage(userId, REMOVE_SETTINGS, "ON_USER_STOPPING", 0 /*delayMillis*/);
+            sendUserMessage(userId, REMOVE_SETTINGS, EVENT_ON_USER_STOPPING, 0 /*delayMillis*/);
         }
 
         if (mGameServiceController != null) {
@@ -1503,7 +1548,7 @@
         // we want to re-populate the setting when switching user as the device config may have
         // changed, which will only update for the previous user, see
         // DeviceConfigListener#onPropertiesChanged.
-        sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_SWITCHING",
+        sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, EVENT_ON_USER_SWITCHING,
                 0 /*delayMillis*/);
 
         if (mGameServiceController != null) {
@@ -1587,6 +1632,34 @@
     public void setGameModeConfigOverride(String packageName, @UserIdInt int userId,
             @GameMode int gameMode, String fpsStr, String scaling) throws SecurityException {
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+        int gameUid = -1;
+        try {
+            gameUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+        } catch (NameNotFoundException ex) {
+            Slog.d(TAG, "Cannot find the UID for package " + packageName + " under user " + userId);
+        }
+        GamePackageConfiguration pkgConfig = getConfig(packageName, userId);
+        if (pkgConfig != null && pkgConfig.getGameModeConfiguration(gameMode) != null) {
+            final GamePackageConfiguration.GameModeConfiguration currentModeConfig =
+                    pkgConfig.getGameModeConfiguration(gameMode);
+            FrameworkStatsLog.write(FrameworkStatsLog.GAME_MODE_CONFIGURATION_CHANGED, gameUid,
+                    Binder.getCallingUid(), gameModeToStatsdGameMode(gameMode),
+                    currentModeConfig.getScaling() /* fromScaling */,
+                    scaling == null ? currentModeConfig.getScaling()
+                            : Float.parseFloat(scaling) /* toScaling */,
+                    currentModeConfig.getFps() /* fromFps */,
+                    fpsStr == null ? currentModeConfig.getFps()
+                            : Integer.parseInt(fpsStr)) /* toFps */;
+        } else {
+            FrameworkStatsLog.write(FrameworkStatsLog.GAME_MODE_CONFIGURATION_CHANGED, gameUid,
+                    Binder.getCallingUid(), gameModeToStatsdGameMode(gameMode),
+                    GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING /* fromScaling*/,
+                    scaling == null ? GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING
+                            : Float.parseFloat(scaling) /* toScaling */,
+                    0 /* fromFps */,
+                    fpsStr == null ? 0 : Integer.parseInt(fpsStr) /* toFps */);
+        }
+
         // Adding game mode config override of the given package name
         GamePackageConfiguration configOverride;
         synchronized (mLock) {
@@ -1629,11 +1702,6 @@
     public void resetGameModeConfigOverride(String packageName, @UserIdInt int userId,
             @GameMode int gameModeToReset) throws SecurityException {
         checkPermission(Manifest.permission.MANAGE_GAME_MODE);
-        final GamePackageConfiguration deviceConfig;
-        synchronized (mDeviceConfigLock) {
-            deviceConfig = mConfigs.get(packageName);
-        }
-
         // resets GamePackageConfiguration of a given packageName.
         // If a gameMode is specified, only reset the GameModeConfiguration of the gameMode.
         synchronized (mLock) {
@@ -1949,13 +2017,99 @@
         LocalServices.addService(GameManagerInternal.class, new LocalService());
     }
 
-    private String dumpDeviceConfigs() {
-        StringBuilder out = new StringBuilder();
-        for (String key : mConfigs.keySet()) {
-            out.append("[\nName: ").append(key)
-                    .append("\nConfig: ").append(mConfigs.get(key).toString()).append("\n]");
+    private void registerStatsCallbacks() {
+        final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.GAME_MODE_INFO,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                this::onPullAtom);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.GAME_MODE_CONFIGURATION,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                this::onPullAtom);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.GAME_MODE_LISTENER,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                this::onPullAtom);
+    }
+
+    private int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+        if (atomTag == FrameworkStatsLog.GAME_MODE_INFO
+                || atomTag == FrameworkStatsLog.GAME_MODE_CONFIGURATION) {
+            int userId = ActivityManager.getCurrentUser();
+            Set<String> packages;
+            synchronized (mDeviceConfigLock) {
+                packages = mConfigs.keySet();
+            }
+            for (String p : packages) {
+                GamePackageConfiguration config = getConfig(p, userId);
+                if (config == null) {
+                    continue;
+                }
+                int uid = -1;
+                try {
+                    uid = mPackageManager.getPackageUidAsUser(p, userId);
+                } catch (NameNotFoundException ex) {
+                    Slog.d(TAG,
+                            "Cannot find UID for package " + p + " under user handle id " + userId);
+                }
+                if (atomTag == FrameworkStatsLog.GAME_MODE_INFO) {
+                    data.add(
+                            FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.GAME_MODE_INFO, uid,
+                                    gameModesToStatsdGameModes(config.getOptedInGameModes()),
+                                    gameModesToStatsdGameModes(config.getAvailableGameModes())));
+                } else if (atomTag == FrameworkStatsLog.GAME_MODE_CONFIGURATION) {
+                    for (int gameMode : config.getAvailableGameModes()) {
+                        GamePackageConfiguration.GameModeConfiguration modeConfig =
+                                config.getGameModeConfiguration(gameMode);
+                        if (modeConfig != null) {
+                            data.add(FrameworkStatsLog.buildStatsEvent(
+                                    FrameworkStatsLog.GAME_MODE_CONFIGURATION, uid,
+                                    gameModeToStatsdGameMode(gameMode), modeConfig.getFps(),
+                                    modeConfig.getScaling()));
+                        }
+                    }
+                }
+            }
+        } else if (atomTag == FrameworkStatsLog.GAME_MODE_LISTENER) {
+            synchronized (mGameModeListenerLock) {
+                data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.GAME_MODE_LISTENER,
+                        mGameModeListeners.size()));
+            }
         }
-        return out.toString();
+        return android.app.StatsManager.PULL_SUCCESS;
+    }
+
+    private static int[] gameModesToStatsdGameModes(int[] modes) {
+        if (modes == null) {
+            return null;
+        }
+        int[] statsdModes = new int[modes.length];
+        int i = 0;
+        for (int mode : modes) {
+            statsdModes[i++] = gameModeToStatsdGameMode(mode);
+        }
+        return statsdModes;
+    }
+
+    private static int gameModeToStatsdGameMode(int mode) {
+        switch (mode) {
+            case GameManager.GAME_MODE_BATTERY:
+                return FrameworkStatsLog.GAME_MODE_CONFIGURATION__GAME_MODE__GAME_MODE_BATTERY;
+            case GameManager.GAME_MODE_PERFORMANCE:
+                return FrameworkStatsLog.GAME_MODE_CONFIGURATION__GAME_MODE__GAME_MODE_PERFORMANCE;
+            case GameManager.GAME_MODE_CUSTOM:
+                return FrameworkStatsLog.GAME_MODE_CONFIGURATION__GAME_MODE__GAME_MODE_CUSTOM;
+            case GameManager.GAME_MODE_STANDARD:
+                return FrameworkStatsLog.GAME_MODE_CONFIGURATION__GAME_MODE__GAME_MODE_STANDARD;
+            case GameManager.GAME_MODE_UNSUPPORTED:
+                return FrameworkStatsLog.GAME_MODE_CONFIGURATION__GAME_MODE__GAME_MODE_UNSUPPORTED;
+            default:
+                return FrameworkStatsLog.GAME_MODE_CONFIGURATION__GAME_MODE__GAME_MODE_UNSPECIFIED;
+        }
     }
 
     private static int gameStateModeToStatsdGameState(int mode) {
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 638bc4e..5189017 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -19,6 +19,7 @@
 import android.app.GameManager;
 import android.os.FileUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
@@ -37,7 +38,6 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.Map;
 
 /**
  * Persists all GameService related settings.
@@ -49,7 +49,11 @@
     // The XML file follows the below format:
     // <?xml>
     // <packages>
-    //     <package></package>
+    //     <package name="" gameMode="">
+    //       <gameModeConfig gameMode="" fps="" scaling="" useAngle="" loadingBoost="">
+    //       </gameModeConfig>
+    //       ...
+    //     </package>
     //     ...
     // </packages>
     private static final String GAME_SERVICE_FILE_NAME = "game-manager-service.xml";
@@ -155,11 +159,14 @@
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             serializer.startTag(null, TAG_PACKAGES);
-            for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) {
-                String packageName = entry.getKey();
+            final ArraySet<String> packageNames = new ArraySet<>(mGameModes.keySet());
+            packageNames.addAll(mConfigOverrides.keySet());
+            for (String packageName : packageNames) {
                 serializer.startTag(null, TAG_PACKAGE);
                 serializer.attribute(null, ATTR_NAME, packageName);
-                serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue());
+                if (mGameModes.containsKey(packageName)) {
+                    serializer.attributeInt(null, ATTR_GAME_MODE, mGameModes.get(packageName));
+                }
                 writeGameModeConfigTags(serializer, mConfigOverrides.get(packageName));
                 serializer.endTag(null, TAG_PACKAGE);
             }
@@ -224,7 +231,7 @@
                 // Do nothing
             }
             if (type != XmlPullParser.START_TAG) {
-                Slog.wtf(TAG, "No start tag found in package manager settings");
+                Slog.wtf(TAG, "No start tag found in game manager settings");
                 return false;
             }
 
@@ -245,7 +252,7 @@
                 }
             }
         } catch (XmlPullParserException | java.io.IOException e) {
-            Slog.wtf(TAG, "Error reading package manager settings", e);
+            Slog.wtf(TAG, "Error reading game manager settings", e);
             return false;
         }
         return true;
@@ -260,15 +267,12 @@
             XmlUtils.skipCurrentTag(parser);
             return;
         }
-        int gameMode;
         try {
-            gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
+            final int gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
+            mGameModes.put(name, gameMode);
         } catch (XmlPullParserException e) {
-            Slog.wtf(TAG, "Invalid game mode in package tag: "
-                    + parser.getAttributeValue(null, ATTR_GAME_MODE), e);
-            return;
+            Slog.v(TAG, "No game mode selected by user for package" + name);
         }
-        mGameModes.put(name, gameMode);
         final int packageTagDepth = parser.getDepth();
         int type;
         final GamePackageConfiguration config = new GamePackageConfiguration(name);
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index cdbffbe..abab0e7 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -27,6 +27,7 @@
 
 import java.io.PrintWriter;
 import java.util.Locale;
+import java.util.StringJoiner;
 
 /**
  * ShellCommands for GameManagerService.
@@ -34,8 +35,20 @@
  * Use with {@code adb shell cmd game ...}.
  */
 public class GameManagerShellCommand extends ShellCommand {
+    private static final String STANDARD_MODE_STR = "standard";
+    private static final String STANDARD_MODE_NUM = "1";
+    private static final String PERFORMANCE_MODE_STR = "performance";
+    private static final String PERFORMANCE_MODE_NUM = "2";
+    private static final String BATTERY_MODE_STR = "battery";
+    private static final String BATTERY_MODE_NUM = "3";
+    private static final String CUSTOM_MODE_STR = "custom";
+    private static final String CUSTOM_MODE_NUM = "4";
+    private static final String UNSUPPORTED_MODE_STR = "unsupported";
+    private static final String UNSUPPORTED_MODE_NUM = String.valueOf(
+            GameManager.GAME_MODE_UNSUPPORTED);
 
-    public GameManagerShellCommand() {}
+    public GameManagerShellCommand() {
+    }
 
     @Override
     public int onCommand(String cmd) {
@@ -46,10 +59,10 @@
         try {
             switch (cmd) {
                 case "set": {
-                    return runSetGameMode(pw);
+                    return runSetGameModeConfig(pw);
                 }
                 case "reset": {
-                    return runResetGameMode(pw);
+                    return runResetGameModeConfig(pw);
                 }
                 case "mode": {
                     /** The "mode" command allows setting a package's current game mode outside of
@@ -61,10 +74,13 @@
                      *          <PACKAGE_NAME> <CONFIG_STRING>`
                      * see: {@link GameManagerServiceTests#mockDeviceConfigAll()}
                      */
-                    return runGameMode(pw);
+                    return runSetGameMode(pw);
                 }
-                case "list": {
-                    return runGameList(pw);
+                case "list-modes": {
+                    return runListGameModes(pw);
+                }
+                case "list-configs": {
+                    return runListGameModeConfigs(pw);
                 }
                 default:
                     return handleDefaultCommands(cmd);
@@ -75,7 +91,21 @@
         return -1;
     }
 
-    private int runGameList(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+    private int runListGameModes(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+        final String packageName = getNextArgRequired();
+        final GameManagerService gameManagerService = (GameManagerService)
+                ServiceManager.getService(Context.GAME_SERVICE);
+        final StringJoiner sj = new StringJoiner(",");
+        for (int mode : gameManagerService.getAvailableGameModes(packageName,
+                ActivityManager.getCurrentUser())) {
+            sj.add(gameModeIntToString(mode));
+        }
+        pw.println(packageName + " has available game modes: [" + sj + "]");
+        return 0;
+    }
+
+    private int runListGameModeConfigs(PrintWriter pw)
+            throws ServiceNotFoundException, RemoteException {
         final String packageName = getNextArgRequired();
 
         final GameManagerService gameManagerService = (GameManagerService)
@@ -92,7 +122,7 @@
         return 0;
     }
 
-    private int runGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+    private int runSetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
         final String option = getNextOption();
         String userIdStr = null;
         if (option != null && option.equals("--user")) {
@@ -105,7 +135,9 @@
                 ServiceManager.getServiceOrThrow(Context.GAME_SERVICE));
         boolean batteryModeSupported = false;
         boolean perfModeSupported = false;
-        int[] modes = service.getAvailableGameModes(packageName);
+        int userId = userIdStr != null ? Integer.parseInt(userIdStr)
+                : ActivityManager.getCurrentUser();
+        int[] modes = service.getAvailableGameModes(packageName, userId);
         for (int mode : modes) {
             if (mode == GameManager.GAME_MODE_PERFORMANCE) {
                 perfModeSupported = true;
@@ -113,37 +145,47 @@
                 batteryModeSupported = true;
             }
         }
-        int userId = userIdStr != null ? Integer.parseInt(userIdStr)
-                : ActivityManager.getCurrentUser();
         switch (gameMode.toLowerCase()) {
-            case "1":
-            case "standard":
+            case STANDARD_MODE_NUM:
+            case STANDARD_MODE_STR:
                 // Standard mode can be used to specify loading ANGLE as the default OpenGL ES
                 // driver, so it should always be available.
                 service.setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
+                pw.println("Set game mode to `STANDARD` for user `" + userId + "` in game `"
+                        + packageName + "`");
                 break;
-            case "2":
-            case "performance":
+            case PERFORMANCE_MODE_NUM:
+            case PERFORMANCE_MODE_STR:
                 if (perfModeSupported) {
                     service.setGameMode(packageName, GameManager.GAME_MODE_PERFORMANCE,
                             userId);
+                    pw.println("Set game mode to `PERFORMANCE` for user `" + userId + "` in game `"
+                            + packageName + "`");
                 } else {
                     pw.println("Game mode: " + gameMode + " not supported by "
                             + packageName);
                     return -1;
                 }
                 break;
-            case "3":
-            case "battery":
+            case BATTERY_MODE_NUM:
+            case BATTERY_MODE_STR:
                 if (batteryModeSupported) {
                     service.setGameMode(packageName, GameManager.GAME_MODE_BATTERY,
                             userId);
+                    pw.println("Set game mode to `BATTERY` for user `" + userId + "` in game `"
+                            + packageName + "`");
                 } else {
                     pw.println("Game mode: " + gameMode + " not supported by "
                             + packageName);
                     return -1;
                 }
                 break;
+            case CUSTOM_MODE_NUM:
+            case CUSTOM_MODE_STR:
+                service.setGameMode(packageName, GameManager.GAME_MODE_CUSTOM, userId);
+                pw.println("Set game mode to `CUSTOM` for user `" + userId + "` in game `"
+                        + packageName + "`");
+                break;
             default:
                 pw.println("Invalid game mode: " + gameMode);
                 return -1;
@@ -151,15 +193,9 @@
         return 0;
     }
 
-    private int runSetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
-        String option = getNextArgRequired();
-        if (!option.equals("--mode")) {
-            pw.println("Invalid option '" + option + "'");
-            return -1;
-        }
-
-        final String gameMode = getNextArgRequired();
-
+    private int runSetGameModeConfig(PrintWriter pw)
+            throws ServiceNotFoundException, RemoteException {
+        String option;
         /**
          * handling optional input
          * "--user", "--downscale" and "--fps" can come in any order
@@ -167,8 +203,12 @@
         String userIdStr = null;
         String fpsStr = null;
         String downscaleRatio = null;
+        int gameMode = GameManager.GAME_MODE_CUSTOM;
         while ((option = getNextOption()) != null) {
             switch (option) {
+                case "--mode":
+                    gameMode = Integer.parseInt(getNextArgRequired());
+                    break;
                 case "--user":
                     if (userIdStr == null) {
                         userIdStr = getNextArgRequired();
@@ -220,50 +260,21 @@
 
         final GameManagerService gameManagerService = (GameManagerService)
                 ServiceManager.getService(Context.GAME_SERVICE);
-
-        boolean batteryModeSupported = false;
-        boolean perfModeSupported = false;
-        int [] modes = gameManagerService.getAvailableGameModes(packageName);
-
-        for (int mode : modes) {
-            if (mode == GameManager.GAME_MODE_PERFORMANCE) {
-                perfModeSupported = true;
-            } else if (mode == GameManager.GAME_MODE_BATTERY) {
-                batteryModeSupported = true;
-            }
+        if (gameManagerService == null) {
+            pw.println("Failed to find GameManagerService on device");
+            return -1;
         }
-
-        switch (gameMode.toLowerCase(Locale.getDefault())) {
-            case "2":
-            case "performance":
-                if (perfModeSupported) {
-                    gameManagerService.setGameModeConfigOverride(packageName, userId,
-                            GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
-                } else {
-                    pw.println("Game mode: " + gameMode + " not supported by "
-                            + packageName);
-                    return -1;
-                }
-                break;
-            case "3":
-            case "battery":
-                if (batteryModeSupported) {
-                    gameManagerService.setGameModeConfigOverride(packageName, userId,
-                            GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
-                } else {
-                    pw.println("Game mode: " + gameMode + " not supported by "
-                            + packageName);
-                    return -1;
-                }
-                break;
-            default:
-                pw.println("Invalid game mode: " + gameMode);
-                return -1;
-        }
+        gameManagerService.setGameModeConfigOverride(packageName, userId, gameMode,
+                fpsStr, downscaleRatio);
+        pw.println("Set custom mode intervention config for user `" + userId + "` in game `"
+                + packageName + "` as: `"
+                + "downscaling-ratio: " + downscaleRatio + ";"
+                + "fps-override: " + fpsStr + "`");
         return 0;
     }
 
-    private int runResetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+    private int runResetGameModeConfig(PrintWriter pw)
+            throws ServiceNotFoundException, RemoteException {
         String option = null;
         String gameMode = null;
         String userIdStr = null;
@@ -305,13 +316,13 @@
         }
 
         switch (gameMode.toLowerCase(Locale.getDefault())) {
-            case "2":
-            case "performance":
+            case PERFORMANCE_MODE_NUM:
+            case PERFORMANCE_MODE_STR:
                 gameManagerService.resetGameModeConfigOverride(packageName, userId,
                         GameManager.GAME_MODE_PERFORMANCE);
                 break;
-            case "3":
-            case "battery":
+            case BATTERY_MODE_NUM:
+            case BATTERY_MODE_STR:
                 gameManagerService.resetGameModeConfigOverride(packageName, userId,
                         GameManager.GAME_MODE_BATTERY);
                 break;
@@ -322,6 +333,22 @@
         return 0;
     }
 
+    private static String gameModeIntToString(@GameManager.GameMode int gameMode) {
+        switch (gameMode) {
+            case GameManager.GAME_MODE_BATTERY:
+                return BATTERY_MODE_STR;
+            case GameManager.GAME_MODE_PERFORMANCE:
+                return PERFORMANCE_MODE_STR;
+            case GameManager.GAME_MODE_CUSTOM:
+                return CUSTOM_MODE_STR;
+            case GameManager.GAME_MODE_STANDARD:
+                return STANDARD_MODE_STR;
+            case GameManager.GAME_MODE_UNSUPPORTED:
+                return UNSUPPORTED_MODE_STR;
+        }
+        return "";
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -329,21 +356,28 @@
         pw.println("  help");
         pw.println("      Print this help text.");
         pw.println("  downscale");
-        pw.println("      Deprecated. Please use `set` command.");
-        pw.println("  mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
+        pw.println("      Deprecated. Please use `custom` command.");
+        pw.println("  list-configs <PACKAGE_NAME>");
+        pw.println("      Lists the current intervention configs of an app.");
+        pw.println("  list-modes <PACKAGE_NAME>");
+        pw.println("      Lists the current available game modes of an app.");
+        pw.println("  mode [--user <USER_ID>] [1|2|3|4|standard|performance|battery|custom] "
+                + "<PACKAGE_NAME>");
         pw.println("      Set app to run in the specified game mode, if supported.");
         pw.println("      --user <USER_ID>: apply for the given user,");
         pw.println("                        the current user is used when unspecified.");
-        pw.println("  set --mode [2|3|performance|battery] [intervention configs] <PACKAGE_NAME>");
-        pw.println("      Set app to run at given game mode with configs, if supported.");
+        pw.println("  set [intervention configs] <PACKAGE_NAME>");
+        pw.println("      Set app to run at custom mode using provided intervention configs");
         pw.println("      Intervention configs consists of:");
         pw.println("      --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
-        pw.println("                  |0.7|0.75|0.8|0.85|0.9|disable]");
-        pw.println("      Set app to run at the specified scaling ratio.");
-        pw.println("      --fps [30|45|60|90|120|disable]");
-        pw.println("      Set app to run at the specified fps, if supported.");
+        pw.println("                  |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
+        pw.println("                                                   specified scaling ratio.");
+        pw.println("      --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
+        pw.println("                                       if supported.");
         pw.println("  reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
         pw.println("      Resets the game mode of the app to device configuration.");
+        pw.println("      This should only be used to reset any override to non custom game mode");
+        pw.println("      applied using the deprecated `set` command");
         pw.println("      --mode [2|3|performance|battery]: apply for the given mode,");
         pw.println("                                        resets all modes when unspecified.");
         pw.println("      --user <USER_ID>: apply for the given user,");
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index a8e4034..408fba1 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.broadcastradio.hal2.AnnouncementAggregator;
 
@@ -51,15 +52,17 @@
     private final Object mLock = new Object();
 
     private final BroadcastRadioService mService;
+
+    @GuardedBy("mLock")
     private final List<RadioManager.ModuleProperties> mV1Modules;
 
     IRadioServiceHidlImpl(BroadcastRadioService service) {
         mService = Objects.requireNonNull(service, "broadcast radio service cannot be null");
-        mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock);
+        mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
         mV1Modules = mHal1.loadModules();
         OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
         mHal2 = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
-                max.isPresent() ? max.getAsInt() + 1 : 0, mLock);
+                max.isPresent() ? max.getAsInt() + 1 : 0);
     }
 
     @VisibleForTesting
@@ -78,9 +81,11 @@
     public List<RadioManager.ModuleProperties> listModules() {
         mService.enforcePolicyAccess();
         Collection<RadioManager.ModuleProperties> v2Modules = mHal2.listModules();
-        List<RadioManager.ModuleProperties> modules = new ArrayList<>(
-                mV1Modules.size() + v2Modules.size());
-        modules.addAll(mV1Modules);
+        List<RadioManager.ModuleProperties> modules;
+        synchronized (mLock) {
+            modules = new ArrayList<>(mV1Modules.size() + v2Modules.size());
+            modules.addAll(mV1Modules);
+        }
         modules.addAll(v2Modules);
         return modules;
     }
@@ -131,7 +136,9 @@
         radioPw.printf("HAL1: %s\n", mHal1);
 
         radioPw.increaseIndent();
-        radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+        synchronized (mLock) {
+            radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+        }
         radioPw.decreaseIndent();
 
         radioPw.printf("HAL2:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 1d71121..03acf72 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -77,7 +77,7 @@
                 }
 
                 RadioModule radioModule =
-                        RadioModule.tryLoadingModule(moduleId, name, newBinder, mLock);
+                        RadioModule.tryLoadingModule(moduleId, name, newBinder);
                 if (radioModule == null) {
                     Slogf.w(TAG, "No module %s with id %d (HAL AIDL)", name, moduleId);
                     return;
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index eb9dafb..e956a9c 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -55,7 +55,7 @@
 
     private final IBroadcastRadio mService;
 
-    private final Object mLock;
+    private final Object mLock = new Object();
     private final Handler mHandler;
     private final RadioLogger mLogger;
     private final RadioManager.ModuleProperties mProperties;
@@ -165,18 +165,15 @@
     };
 
     @VisibleForTesting
-    RadioModule(IBroadcastRadio service,
-            RadioManager.ModuleProperties properties, Object lock) {
+    RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties) {
         mProperties = Objects.requireNonNull(properties, "properties cannot be null");
         mService = Objects.requireNonNull(service, "service cannot be null");
-        mLock = Objects.requireNonNull(lock, "lock cannot be null");
         mHandler = new Handler(Looper.getMainLooper());
         mLogger = new RadioLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
     }
 
     @Nullable
-    static RadioModule tryLoadingModule(int moduleId, String moduleName,
-            IBinder serviceBinder, Object lock) {
+    static RadioModule tryLoadingModule(int moduleId, String moduleName, IBinder serviceBinder) {
         try {
             Slogf.i(TAG, "Try loading module for module id = %d, module name = %s",
                     moduleId, moduleName);
@@ -206,7 +203,7 @@
             RadioManager.ModuleProperties prop = ConversionUtils.propertiesFromHalProperties(
                     moduleId, moduleName, service.getProperties(), amfmConfig, dabConfig);
 
-            return new RadioModule(service, prop, lock);
+            return new RadioModule(service, prop);
         } catch (RemoteException ex) {
             Slogf.e(TAG, ex, "Failed to load module %s", moduleName);
             return null;
@@ -222,9 +219,7 @@
     }
 
     void setInternalHalCallback() throws RemoteException {
-        synchronized (mLock) {
-            mService.setTunerCallback(mHalTunerCallback);
-        }
+        mService.setTunerCallback(mHalTunerCallback);
     }
 
     TunerSession openSession(android.hardware.radio.ITunerCallback userCb)
@@ -234,7 +229,7 @@
         Boolean antennaConnected;
         RadioManager.ProgramInfo currentProgramInfo;
         synchronized (mLock) {
-            tunerSession = new TunerSession(this, mService, userCb, mLock);
+            tunerSession = new TunerSession(this, mService, userCb);
             mAidlTunerSessions.add(tunerSession);
             antennaConnected = mAntennaConnected;
             currentProgramInfo = mCurrentProgramInfo;
@@ -356,14 +351,14 @@
             // Otherwise, update the HAL's filter, and AIDL clients will be updated when
             // mHalTunerCallback.onProgramListUpdated() is called.
             mUnionOfAidlProgramFilters = newFilter;
-            try {
-                mService.startProgramListUpdates(
-                        ConversionUtils.filterToHalProgramFilter(newFilter));
-            } catch (RuntimeException ex) {
-                throw ConversionUtils.throwOnError(ex, /* action= */ "Start Program ListUpdates");
-            } catch (RemoteException ex) {
-                Slogf.e(TAG, ex, "mHalTunerSession.startProgramListUpdates() failed");
-            }
+        }
+        try {
+            mService.startProgramListUpdates(
+                    ConversionUtils.filterToHalProgramFilter(newFilter));
+        } catch (RuntimeException ex) {
+            throw ConversionUtils.throwOnError(ex, /* action= */ "Start Program ListUpdates");
+        } catch (RemoteException ex) {
+            Slogf.e(TAG, ex, "mHalTunerSession.startProgramListUpdates() failed");
         }
     }
 
@@ -453,12 +448,10 @@
             }
         };
 
-        synchronized (mLock) {
-            try {
-                hwCloseHandle[0] = mService.registerAnnouncementListener(hwListener, enabledList);
-            } catch (RuntimeException ex) {
-                throw ConversionUtils.throwOnError(ex, /* action= */ "AnnouncementListener");
-            }
+        try {
+            hwCloseHandle[0] = mService.registerAnnouncementListener(hwListener, enabledList);
+        } catch (RuntimeException ex) {
+            throw ConversionUtils.throwOnError(ex, /* action= */ "AnnouncementListener");
         }
 
         return new android.hardware.radio.ICloseHandle.Stub() {
@@ -478,12 +471,10 @@
         if (id == 0) throw new IllegalArgumentException("Image ID is missing");
 
         byte[] rawImage;
-        synchronized (mLock) {
-            try {
-                rawImage = mService.getImage(id);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            rawImage = mService.getImage(id);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
 
         if (rawImage == null || rawImage.length == 0) return null;
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index d33633c..1ce4044 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -42,7 +42,7 @@
     private static final String TAG = "BcRadioAidlSrv.session";
     private static final int TUNER_EVENT_LOGGER_QUEUE_SIZE = 25;
 
-    private final Object mLock;
+    private final Object mLock = new Object();
 
     private final RadioLogger mLogger;
     private final RadioModule mModule;
@@ -61,12 +61,10 @@
     private RadioManager.BandConfig mPlaceHolderConfig;
 
     TunerSession(RadioModule radioModule, IBroadcastRadio service,
-            android.hardware.radio.ITunerCallback callback,
-            Object lock) {
+            android.hardware.radio.ITunerCallback callback) {
         mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null");
         mService = Objects.requireNonNull(service, "service cannot be null");
         mCallback = Objects.requireNonNull(callback, "callback cannot be null");
-        mLock = Objects.requireNonNull(lock, "lock cannot be null");
         mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
     }
 
@@ -91,17 +89,19 @@
             mLogger.logRadioEvent("Close tuner session on error %d", error);
         }
         synchronized (mLock) {
-            if (mIsClosed) return;
-            if (error != null) {
-                try {
-                    mCallback.onError(error);
-                } catch (RemoteException ex) {
-                    Slogf.w(TAG, ex, "mCallback.onError(%s) failed", error);
-                }
+            if (mIsClosed) {
+                return;
             }
             mIsClosed = true;
-            mModule.onTunerSessionClosed(this);
         }
+        if (error != null) {
+            try {
+                mCallback.onError(error);
+            } catch (RemoteException ex) {
+                Slogf.w(TAG, ex, "mCallback.onError(%s) failed", error);
+            }
+        }
+        mModule.onTunerSessionClosed(this);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
index e50c6e8..fb42c94 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -36,7 +36,7 @@
      */
     private final long mNativeContext = nativeInit();
 
-    private final Object mLock;
+    private final Object mLock = new Object();
 
     @Override
     protected void finalize() throws Throwable {
@@ -50,14 +50,6 @@
     private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
             RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
 
-    /**
-     * Constructor. should pass
-     * {@code com.android.server.broadcastradio.BroadcastRadioService#mLock} for lock.
-     */
-    public BroadcastRadioService(Object lock) {
-        mLock = lock;
-    }
-
     public @NonNull List<RadioManager.ModuleProperties> loadModules() {
         synchronized (mLock) {
             return Objects.requireNonNull(nativeLoadModules(mNativeContext));
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 3d69627..984bf51 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -43,13 +43,16 @@
 import java.util.Objects;
 import java.util.stream.Collectors;
 
-public class BroadcastRadioService {
+/**
+ * Broadcast radio service using BroadcastRadio HIDL 2.0 HAL
+ */
+public final class BroadcastRadioService {
     private static final String TAG = "BcRadio2Srv";
 
-    private final Object mLock;
+    private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private int mNextModuleId = 0;
+    private int mNextModuleId;
 
     @GuardedBy("mLock")
     private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>();
@@ -72,7 +75,7 @@
                     moduleId = mNextModuleId;
                 }
 
-                RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName, mLock);
+                RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName);
                 if (module == null) {
                     return;
                 }
@@ -120,9 +123,8 @@
         }
     };
 
-    public BroadcastRadioService(int nextModuleId, Object lock) {
+    public BroadcastRadioService(int nextModuleId) {
         mNextModuleId = nextModuleId;
-        mLock = lock;
         try {
             IServiceManager manager = IServiceManager.getService();
             if (manager == null) {
@@ -136,9 +138,8 @@
     }
 
     @VisibleForTesting
-    BroadcastRadioService(int nextModuleId, Object lock, IServiceManager manager) {
+    BroadcastRadioService(int nextModuleId, IServiceManager manager) {
         mNextModuleId = nextModuleId;
-        mLock = lock;
         Objects.requireNonNull(manager, "Service manager cannot be null");
         try {
             manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
@@ -180,7 +181,7 @@
             throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0");
         }
 
-        RadioModule module = null;
+        RadioModule module;
         synchronized (mLock) {
             module = mModules.get(moduleId);
             if (module == null) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index cf1b504..0ea5f0f 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -60,7 +60,7 @@
     @NonNull private final IBroadcastRadio mService;
     @NonNull private final RadioManager.ModuleProperties mProperties;
 
-    private final Object mLock;
+    private final Object mLock = new Object();
     @NonNull private final Handler mHandler;
     @NonNull private final RadioEventLogger mEventLogger;
 
@@ -75,7 +75,7 @@
     private RadioManager.ProgramInfo mCurrentProgramInfo = null;
 
     @GuardedBy("mLock")
-    private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(null);
+    private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(/* filter= */ null);
 
     @GuardedBy("mLock")
     private android.hardware.radio.ProgramList.Filter mUnionOfAidlProgramFilters = null;
@@ -84,47 +84,59 @@
     private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
         @Override
         public void onTuneFailed(int result, ProgramSelector programSelector) {
-            lockAndFireLater(() -> {
+            fireLater(() -> {
                 android.hardware.radio.ProgramSelector csel =
                         Convert.programSelectorFromHal(programSelector);
-                fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+                synchronized (mLock) {
+                    fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+                }
             });
         }
 
         @Override
         public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
-            lockAndFireLater(() -> {
-                mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
-                fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(mCurrentProgramInfo));
+            fireLater(() -> {
+                synchronized (mLock) {
+                    mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
+                    RadioManager.ProgramInfo currentProgramInfo = mCurrentProgramInfo;
+                    fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(
+                            currentProgramInfo));
+                }
             });
         }
 
         @Override
         public void onProgramListUpdated(ProgramListChunk programListChunk) {
-            lockAndFireLater(() -> {
+            fireLater(() -> {
                 android.hardware.radio.ProgramList.Chunk chunk =
                         Convert.programListChunkFromHal(programListChunk);
-                mProgramInfoCache.filterAndApplyChunk(chunk);
+                synchronized (mLock) {
+                    mProgramInfoCache.filterAndApplyChunk(chunk);
 
-                for (TunerSession tunerSession : mAidlTunerSessions) {
-                    tunerSession.onMergedProgramListUpdateFromHal(chunk);
+                    for (TunerSession tunerSession : mAidlTunerSessions) {
+                        tunerSession.onMergedProgramListUpdateFromHal(chunk);
+                    }
                 }
             });
         }
 
         @Override
         public void onAntennaStateChange(boolean connected) {
-            lockAndFireLater(() -> {
-                mAntennaConnected = connected;
-                fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
+            fireLater(() -> {
+                synchronized (mLock) {
+                    mAntennaConnected = connected;
+                    fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
+                }
             });
         }
 
         @Override
         public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
-            lockAndFireLater(() -> {
+            fireLater(() -> {
                 Map<String, String> cparam = Convert.vendorInfoFromHal(parameters);
-                fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+                synchronized (mLock) {
+                    fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+                }
             });
         }
     };
@@ -135,17 +147,15 @@
 
     @VisibleForTesting
     RadioModule(@NonNull IBroadcastRadio service,
-            @NonNull RadioManager.ModuleProperties properties, @NonNull Object lock) {
+            @NonNull RadioManager.ModuleProperties properties) {
         mProperties = Objects.requireNonNull(properties);
         mService = Objects.requireNonNull(service);
-        mLock = Objects.requireNonNull(lock);
         mHandler = new Handler(Looper.getMainLooper());
         mEventLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
     }
 
     @Nullable
-    static RadioModule tryLoadingModule(int idx, @NonNull String fqName,
-            Object lock) {
+    static RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
         try {
             Slog.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
             IBroadcastRadio service = IBroadcastRadio.getService(fqName);
@@ -167,7 +177,7 @@
             RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
                     service.getProperties(), amfmConfig.value, dabConfig.value);
 
-            return new RadioModule(service, prop, lock);
+            return new RadioModule(service, prop);
         } catch (RemoteException ex) {
             Slog.e(TAG, "Failed to load module " + fqName, ex);
             return null;
@@ -196,8 +206,7 @@
                 });
                 mHalTunerSession = Objects.requireNonNull(hwSession.value);
             }
-            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb,
-                    mLock);
+            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
             mAidlTunerSessions.add(tunerSession);
 
             // Propagate state to new client. Note: These callbacks are invoked while holding mLock
@@ -229,6 +238,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     @Nullable
     private android.hardware.radio.ProgramList.Filter
             buildUnionOfTunerSessionFiltersLocked() {
@@ -281,6 +291,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void onTunerSessionProgramListFilterChangedLocked(@Nullable TunerSession session) {
         android.hardware.radio.ProgramList.Filter newFilter =
                 buildUnionOfTunerSessionFiltersLocked();
@@ -325,6 +336,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void onTunerSessionsClosedLocked(TunerSession... tunerSessions) {
         for (TunerSession tunerSession : tunerSessions) {
             mAidlTunerSessions.remove(tunerSession);
@@ -342,12 +354,8 @@
     }
 
     // add to mHandler queue, but ensure the runnable holds mLock when it gets executed
-    private void lockAndFireLater(Runnable r) {
-        mHandler.post(() -> {
-            synchronized (mLock) {
-                r.run();
-            }
-        });
+    private void fireLater(Runnable r) {
+        mHandler.post(() -> r.run());
     }
 
     interface AidlCallbackRunnable {
@@ -356,9 +364,14 @@
 
     // Invokes runnable with each TunerSession currently open.
     void fanoutAidlCallback(AidlCallbackRunnable runnable) {
-        lockAndFireLater(() -> fanoutAidlCallbackLocked(runnable));
+        fireLater(() -> {
+            synchronized (mLock) {
+                fanoutAidlCallbackLocked(runnable);
+            }
+        });
     }
 
+    @GuardedBy("mLock")
     private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
         List<TunerSession> deadSessions = null;
         for (TunerSession tunerSession : mAidlTunerSessions) {
@@ -399,12 +412,10 @@
             }
         };
 
-        synchronized (mLock) {
-            mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHnd) -> {
-                halResult.value = result;
-                hwCloseHandle.value = closeHnd;
-            });
-        }
+        mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHandle) -> {
+            halResult.value = result;
+            hwCloseHandle.value = closeHandle;
+        });
         Convert.throwOnError("addAnnouncementListener", halResult.value);
 
         return new android.hardware.radio.ICloseHandle.Stub() {
@@ -424,12 +435,10 @@
         if (id == 0) throw new IllegalArgumentException("Image ID is missing");
 
         byte[] rawImage;
-        synchronized (mLock) {
-            List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id));
-            rawImage = new byte[rawList.size()];
-            for (int i = 0; i < rawList.size(); i++) {
-                rawImage[i] = rawList.get(i);
-            }
+        List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id));
+        rawImage = new byte[rawList.size()];
+        for (int i = 0; i < rawList.size(); i++) {
+            rawImage[i] = rawList.get(i);
         }
 
         if (rawImage == null || rawImage.length == 0) return null;
@@ -440,17 +449,17 @@
     void dumpInfo(IndentingPrintWriter pw) {
         pw.printf("RadioModule\n");
         pw.increaseIndent();
+        pw.printf("BroadcastRadioService: %s\n", mService);
+        pw.printf("Properties: %s\n", mProperties);
         synchronized (mLock) {
-            pw.printf("BroadcastRadioService: %s\n", mService);
-            pw.printf("Properties: %s\n", mProperties);
-            pw.printf("HIDL2.0 HAL TunerSession: %s\n", mHalTunerSession);
+            pw.printf("HIDL 2.0 HAL TunerSession: %s\n", mHalTunerSession);
             pw.printf("Is antenna connected? ");
             if (mAntennaConnected == null) {
                 pw.printf("null\n");
             } else {
                 pw.printf("%s\n", mAntennaConnected ? "Yes" : "No");
             }
-            pw.printf("current ProgramInfo: %s\n", mCurrentProgramInfo);
+            pw.printf("Current ProgramInfo: %s\n", mCurrentProgramInfo);
             pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
             pw.printf("Union of AIDL ProgramFilters: %s\n", mUnionOfAidlProgramFilters);
             pw.printf("AIDL TunerSessions:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 12211ee..7afee27 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -33,6 +33,7 @@
 import android.util.MutableInt;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.broadcastradio.RadioServiceUserController;
 import com.android.server.utils.Slogf;
 
@@ -46,26 +47,28 @@
     private static final String kAudioDeviceName = "Radio tuner source";
     private static final int TUNER_EVENT_LOGGER_QUEUE_SIZE = 25;
 
-    private final Object mLock;
+    private final Object mLock = new Object();
     @NonNull private final RadioEventLogger mEventLogger;
 
     private final RadioModule mModule;
     private final ITunerSession mHwSession;
     final android.hardware.radio.ITunerCallback mCallback;
+
+    @GuardedBy("mLock")
     private boolean mIsClosed = false;
+    @GuardedBy("mLock")
     private boolean mIsMuted = false;
+    @GuardedBy("mLock")
     private ProgramInfoCache mProgramInfoCache = null;
 
     // necessary only for older APIs compatibility
     private RadioManager.BandConfig mDummyConfig = null;
 
     TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
-            @NonNull android.hardware.radio.ITunerCallback callback,
-            @NonNull Object lock) {
+            @NonNull android.hardware.radio.ITunerCallback callback) {
         mModule = Objects.requireNonNull(module);
         mHwSession = Objects.requireNonNull(hwSession);
         mCallback = Objects.requireNonNull(callback);
-        mLock = Objects.requireNonNull(lock);
         mEventLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
     }
 
@@ -86,23 +89,26 @@
         mEventLogger.logRadioEvent("Close on error %d", error);
         synchronized (mLock) {
             if (mIsClosed) return;
-            if (error != null) {
-                try {
-                    mCallback.onError(error);
-                } catch (RemoteException ex) {
-                    Slog.w(TAG, "mCallback.onError() failed: ", ex);
-                }
-            }
             mIsClosed = true;
-            mModule.onTunerSessionClosed(this);
         }
+        if (error != null) {
+            try {
+                mCallback.onError(error);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "mCallback.onError() failed: ", ex);
+            }
+        }
+        mModule.onTunerSessionClosed(this);
     }
 
     @Override
     public boolean isClosed() {
-        return mIsClosed;
+        synchronized (mLock) {
+            return mIsClosed;
+        }
     }
 
+    @GuardedBy("mLock")
     private void checkNotClosedLocked() {
         if (mIsClosed) {
             throw new IllegalStateException("Tuner is closed, no further operations are allowed");
@@ -118,9 +124,9 @@
         synchronized (mLock) {
             checkNotClosedLocked();
             mDummyConfig = Objects.requireNonNull(config);
-            Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
-            mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
         }
+        Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
+        mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
     }
 
     @Override
@@ -137,8 +143,8 @@
             checkNotClosedLocked();
             if (mIsMuted == mute) return;
             mIsMuted = mute;
-            Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app");
         }
+        Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app");
     }
 
     @Override
@@ -383,8 +389,8 @@
     void dumpInfo(IndentingPrintWriter pw) {
         pw.printf("TunerSession\n");
         pw.increaseIndent();
+        pw.printf("HIDL HAL Session: %s\n", mHwSession);
         synchronized (mLock) {
-            pw.printf("HIDL HAL Session: %s\n", mHwSession);
             pw.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
             pw.printf("Is muted? %s\n", mIsMuted ? "Yes" : "No");
             pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
diff --git a/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java b/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java
new file mode 100644
index 0000000..06b45bf
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL;
+import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_BACKGROUND;
+
+import com.android.internal.util.Preconditions;
+
+/** CPU availability information. */
+public final class CpuAvailabilityInfo {
+    /** Constant to indicate missing CPU availability percent. */
+    public static final int MISSING_CPU_AVAILABILITY_PERCENT = -1;
+
+    /**
+     * The CPUSET whose availability info is recorded in this object.
+     *
+     * <p>The contained value is one of the CPUSET_* constants from the
+     * {@link CpuAvailabilityMonitoringConfig}.
+     */
+    @CpuAvailabilityMonitoringConfig.Cpuset
+    public final int cpuset;
+
+    /** The latest average CPU availability percent. */
+    public final int latestAvgAvailabilityPercent;
+
+    /** The past N-second average CPU availability percent. */
+    public final int pastNSecAvgAvailabilityPercent;
+
+    /** The duration over which the {@link pastNSecAvgAvailabilityPercent} was calculated. */
+    public final int avgAvailabilityDurationSec;
+
+    @Override
+    public String toString() {
+        return "CpuAvailabilityInfo{" + "cpuset=" + cpuset + ", latestAvgAvailabilityPercent="
+                + latestAvgAvailabilityPercent + ", pastNSecAvgAvailabilityPercent="
+                + pastNSecAvgAvailabilityPercent + ", avgAvailabilityDurationSec="
+                + avgAvailabilityDurationSec + '}';
+    }
+
+    CpuAvailabilityInfo(int cpuset, int latestAvgAvailabilityPercent,
+            int pastNSecAvgAvailabilityPercent, int avgAvailabilityDurationSec) {
+        this.cpuset = Preconditions.checkArgumentInRange(cpuset, CPUSET_ALL, CPUSET_BACKGROUND,
+                "cpuset");
+        this.latestAvgAvailabilityPercent = latestAvgAvailabilityPercent;
+        this.pastNSecAvgAvailabilityPercent = pastNSecAvgAvailabilityPercent;
+        this.avgAvailabilityDurationSec = avgAvailabilityDurationSec;
+    }
+}
diff --git a/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java b/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java
new file mode 100644
index 0000000..a3c4c9e
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import android.annotation.IntDef;
+import android.util.IntArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** CPU availability monitoring config. */
+public final class CpuAvailabilityMonitoringConfig {
+    /** Constant to monitor all cpusets. */
+    public static final int CPUSET_ALL = 1;
+
+    /** Constant to monitor background cpusets. */
+    public static final int CPUSET_BACKGROUND = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CPUSET_"}, value = {
+            CPUSET_ALL,
+            CPUSET_BACKGROUND
+    })
+    public @interface Cpuset {
+    }
+
+    /**
+     * The CPUSET to monitor.
+     *
+     * <p>The value must be one of the {@code CPUSET_*} constants.
+     */
+    @Cpuset
+    public final int cpuset;
+
+    /**
+     * CPU availability percent thresholds.
+     *
+     * <p>CPU availability change notifications are sent when the latest or last N seconds average
+     * CPU availability percent crosses any of these thresholds since the last notification.
+     */
+    private final IntArray mThresholds;
+
+    public IntArray getThresholds() {
+        return mThresholds;
+    }
+
+    /**
+     * Builder for the construction of {@link CpuAvailabilityMonitoringConfig} objects.
+     *
+     * <p>The builder must contain at least one threshold before calling {@link build}.
+     */
+    public static final class Builder {
+        private final int mCpuset;
+        private final IntArray mThresholds = new IntArray();
+
+        public Builder(int cpuset, int... thresholds) {
+            mCpuset = cpuset;
+            for (int threshold : thresholds) {
+                addThreshold(threshold);
+            }
+        }
+
+        /** Adds the given threshold to the builder object. */
+        public Builder addThreshold(int threshold) {
+            if (mThresholds.indexOf(threshold) == -1) {
+                mThresholds.add(threshold);
+            }
+            return this;
+        }
+
+        /** Returns the {@link CpuAvailabilityMonitoringConfig} object. */
+        public CpuAvailabilityMonitoringConfig build() {
+            return new CpuAvailabilityMonitoringConfig(this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "CpuAvailabilityMonitoringConfig{cpuset=" + cpuset + ", mThresholds=" + mThresholds
+                + ')';
+    }
+
+    private CpuAvailabilityMonitoringConfig(Builder builder) {
+        if (builder.mCpuset != CPUSET_ALL && builder.mCpuset != CPUSET_BACKGROUND) {
+            throw new IllegalStateException("Cpuset must be either CPUSET_ALL (" + CPUSET_ALL
+                    + ") or CPUSET_BACKGROUND (" + CPUSET_BACKGROUND + "). Builder contains "
+                    + builder.mCpuset);
+        }
+        if (builder.mThresholds.size() == 0) {
+            throw new IllegalStateException("Must provide at least one threshold");
+        }
+        this.cpuset = builder.mCpuset;
+        this.mThresholds = builder.mThresholds.clone();
+    }
+}
diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java
new file mode 100644
index 0000000..680829d
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.Slogf;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Reader to read CPU information from proc and sys fs files exposed by the Kernel. */
+public final class CpuInfoReader {
+    static final String TAG = CpuInfoReader.class.getSimpleName();
+    static final int FLAG_CPUSET_CATEGORY_TOP_APP = 1 << 0;
+    static final int FLAG_CPUSET_CATEGORY_BACKGROUND = 1 << 1;
+
+    private static final String CPUFREQ_DIR_PATH = "/sys/devices/system/cpu/cpufreq";
+    private static final String POLICY_DIR_PREFIX = "policy";
+    private static final String RELATED_CPUS_FILE = "related_cpus";
+    private static final String MAX_CPUFREQ_FILE = "cpuinfo_max_freq";
+    private static final String MAX_SCALING_FREQ_FILE = "scaling_max_freq";
+    private static final String CPUSET_DIR_PATH = "/dev/cpuset";
+    private static final String CPUSET_TOP_APP_DIR = "top-app";
+    private static final String CPUSET_BACKGROUND_DIR = "background";
+    private static final String CPUS_FILE = "cpus";
+    private static final String PROC_STAT_FILE_PATH = "/proc/stat";
+    private static final Pattern PROC_STAT_PATTERN =
+            Pattern.compile("cpu(?<core>[0-9]+)\\s(?<userClockTicks>[0-9]+)\\s"
+                    + "(?<niceClockTicks>[0-9]+)\\s(?<sysClockTicks>[0-9]+)\\s"
+                    + "(?<idleClockTicks>[0-9]+)\\s(?<iowaitClockTicks>[0-9]+)\\s"
+                    + "(?<irqClockTicks>[0-9]+)\\s(?<softirqClockTicks>[0-9]+)\\s"
+                    + "(?<stealClockTicks>[0-9]+)\\s(?<guestClockTicks>[0-9]+)\\s"
+                    + "(?<guestNiceClockTicks>[0-9]+)");
+    private static final long MILLIS_PER_JIFFY = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"FLAG_CPUSET_CATEGORY_"}, flag = true, value = {
+            FLAG_CPUSET_CATEGORY_TOP_APP,
+            FLAG_CPUSET_CATEGORY_BACKGROUND
+    })
+    private @interface CpusetCategory{}
+
+    private final File mCpusetDir;
+    private final File mCpuFreqDir;
+    private final File mProcStatFile;
+    private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray();
+    private final SparseArray<Long> mMaxCpuFrequenciesByCpus = new SparseArray<>();
+
+    private File[] mCpuFreqPolicyDirs;
+    private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>();
+    private boolean mIsEnabled;
+
+    public CpuInfoReader() {
+        this(new File(CPUSET_DIR_PATH), new File(CPUFREQ_DIR_PATH), new File(PROC_STAT_FILE_PATH));
+    }
+
+    @VisibleForTesting
+    CpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile) {
+        mCpusetDir = cpusetDir;
+        mCpuFreqDir = cpuFreqDir;
+        mProcStatFile = procStatFile;
+    }
+
+    /** Inits CpuInfoReader and returns a boolean to indicate whether the reader is enabled. */
+    public boolean init() {
+        mCpuFreqPolicyDirs = mCpuFreqDir.listFiles(
+                file -> file.isDirectory() && file.getName().startsWith(POLICY_DIR_PREFIX));
+        if (mCpuFreqPolicyDirs == null || mCpuFreqPolicyDirs.length == 0) {
+            Slogf.w(TAG, "Missing CPU frequency policy directories at %s",
+                    mCpuFreqDir.getAbsolutePath());
+            return false;
+        }
+        if (!mProcStatFile.exists()) {
+            Slogf.e(TAG, "Missing proc stat file at %s", mProcStatFile.getAbsolutePath());
+            return false;
+        }
+        readCpusetCategories();
+        if (mCpusetCategoriesByCpus.size() == 0) {
+            Slogf.e(TAG, "Failed to read cpuset information read from %s",
+                    mCpusetDir.getAbsolutePath());
+            return false;
+        }
+        readMaxCpuFrequencies();
+        if (mMaxCpuFrequenciesByCpus.size() == 0) {
+            Slogf.e(TAG, "Failed to read max CPU frequencies from policy directories at %s",
+                    mCpuFreqDir.getAbsolutePath());
+            return false;
+        }
+        mIsEnabled = true;
+        return true;
+    }
+
+    /** Reads CPU information from proc and sys fs files exposed by the Kernel. */
+    public List<CpuInfo> readCpuInfos() {
+        if (!mIsEnabled) {
+            return Collections.emptyList();
+        }
+        SparseArray<CpuUsageStats> latestCpuUsageStats = readLatestCpuUsageStats();
+        if (latestCpuUsageStats == null) {
+            Slogf.e(TAG, "Failed to read latest CPU usage stats");
+            return Collections.emptyList();
+        }
+        // TODO(b/217422127): Read current CPU frequencies and populate the CpuInfo.
+        return Collections.emptyList();
+    }
+
+    private void readCpusetCategories() {
+        File[] cpusetDirs = mCpusetDir.listFiles(File::isDirectory);
+        if (cpusetDirs == null) {
+            Slogf.e(TAG, "Missing cpuset directories at %s", mCpusetDir.getAbsolutePath());
+            return;
+        }
+        for (int i = 0; i < cpusetDirs.length; i++) {
+            File dir = cpusetDirs[i];
+            @CpusetCategory int cpusetCategory;
+            switch (dir.getName()) {
+                case CPUSET_TOP_APP_DIR:
+                    cpusetCategory = FLAG_CPUSET_CATEGORY_TOP_APP;
+                    break;
+                case CPUSET_BACKGROUND_DIR:
+                    cpusetCategory = FLAG_CPUSET_CATEGORY_BACKGROUND;
+                    break;
+                default:
+                    continue;
+            }
+            File cpuCoresFile = new File(dir.getPath(), CPUS_FILE);
+            List<Integer> cpuCores = readCpuCores(cpuCoresFile);
+            if (cpuCores.isEmpty()) {
+                Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
+                continue;
+            }
+            for (int j = 0; j < cpuCores.size(); j++) {
+                int categories = mCpusetCategoriesByCpus.get(cpuCores.get(j));
+                categories |= cpusetCategory;
+                mCpusetCategoriesByCpus.append(cpuCores.get(j), categories);
+            }
+        }
+    }
+
+    private void readMaxCpuFrequencies() {
+        for (int i = 0; i < mCpuFreqPolicyDirs.length; i++) {
+            File policyDir = mCpuFreqPolicyDirs[i];
+            long maxCpuFreqKHz = readMaxCpuFrequency(policyDir);
+            if (maxCpuFreqKHz == 0) {
+                Slogf.w(TAG, "Invalid max CPU frequency read from %s", policyDir.getAbsolutePath());
+                continue;
+            }
+            File cpuCoresFile = new File(policyDir, RELATED_CPUS_FILE);
+            List<Integer> cpuCores = readCpuCores(cpuCoresFile);
+            if (cpuCores.isEmpty()) {
+                Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
+                continue;
+            }
+            for (int j = 0; j < cpuCores.size(); j++) {
+                mMaxCpuFrequenciesByCpus.append(cpuCores.get(j), maxCpuFreqKHz);
+            }
+        }
+    }
+
+    private long readMaxCpuFrequency(File policyDir) {
+        long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE));
+        return curCpuFreqKHz > 0 ? curCpuFreqKHz
+                : readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE));
+    }
+
+    private static long readCpuFreqKHz(File file) {
+        if (!file.exists()) {
+            Slogf.e(TAG, "CPU frequency file %s doesn't exist", file.getAbsolutePath());
+            return 0;
+        }
+        try {
+            List<String> lines = Files.readAllLines(file.toPath());
+            if (!lines.isEmpty()) {
+                long frequency = Long.parseLong(lines.get(0).trim());
+                return frequency > 0 ? frequency : 0;
+            }
+        } catch (Exception e) {
+            Slogf.e(TAG, e, "Failed to read integer content from file: %s", file.getAbsolutePath());
+        }
+        return 0;
+    }
+
+    /**
+     * Reads the list of CPU cores from the given file.
+     *
+     * Reads CPU cores represented in one of the below formats.
+     * <ul>
+     * <li> Single core id. Eg: 1
+     * <li> Core id range. Eg: 1-4
+     * <li> Comma separated values. Eg: 1, 3-5, 7
+     * </ul>
+     */
+    private static List<Integer> readCpuCores(File file) {
+        if (!file.exists()) {
+            Slogf.e(TAG, "Failed to read CPU cores as the file '%s' doesn't exist",
+                    file.getAbsolutePath());
+            return Collections.emptyList();
+        }
+        try {
+            List<String> lines = Files.readAllLines(file.toPath());
+            List<Integer> cpuCores = new ArrayList<>();
+            for (int i = 0; i < lines.size(); i++) {
+                String[] pairs = lines.get(i).trim().split(",");
+                for (int j = 0; j < pairs.length; j++) {
+                    String[] minMaxPairs = pairs[j].split("-");
+                    if (minMaxPairs.length >= 2) {
+                        int min = Integer.parseInt(minMaxPairs[0]);
+                        int max = Integer.parseInt(minMaxPairs[1]);
+                        if (min > max) {
+                            continue;
+                        }
+                        for (int id = min; id <= max; id++) {
+                            cpuCores.add(id);
+                        }
+                    } else if (minMaxPairs.length == 1) {
+                        cpuCores.add(Integer.parseInt(minMaxPairs[0]));
+                    } else {
+                        Slogf.w(TAG, "Invalid CPU core range format %s", pairs[j]);
+                    }
+                }
+            }
+            return cpuCores;
+        } catch (Exception e) {
+            Slogf.e(TAG, e, "Failed to read CPU cores from %s", file.getAbsolutePath());
+        }
+        return Collections.emptyList();
+    }
+
+    @Nullable
+    private SparseArray<CpuUsageStats> readLatestCpuUsageStats() {
+        SparseArray<CpuUsageStats> cumulativeCpuUsageStats = readCumulativeCpuUsageStats();
+        if (cumulativeCpuUsageStats.size() == 0) {
+            Slogf.e(TAG, "Failed to read cumulative CPU usage stats");
+            return null;
+        }
+        SparseArray<CpuUsageStats> deltaCpuUsageStats = new SparseArray();
+        for (int i = 0; i < cumulativeCpuUsageStats.size(); i++) {
+            int cpu = cumulativeCpuUsageStats.keyAt(i);
+            CpuUsageStats newStats = cumulativeCpuUsageStats.valueAt(i);
+            CpuUsageStats oldStats = mCumulativeCpuUsageStats.get(cpu);
+            deltaCpuUsageStats.append(cpu, oldStats == null ? newStats : newStats.delta(oldStats));
+        }
+        mCumulativeCpuUsageStats = cumulativeCpuUsageStats;
+        return deltaCpuUsageStats;
+    }
+
+    private SparseArray<CpuUsageStats> readCumulativeCpuUsageStats() {
+        SparseArray<CpuUsageStats> cpuUsageStats = new SparseArray<>();
+        try {
+            List<String> lines = Files.readAllLines(mProcStatFile.toPath());
+            for (int i = 0; i < lines.size(); i++) {
+                Matcher m = PROC_STAT_PATTERN.matcher(lines.get(i).trim());
+                if (!m.find()) {
+                    continue;
+                }
+                cpuUsageStats.append(Integer.parseInt(Objects.requireNonNull(m.group("core"))),
+                        new CpuUsageStats(jiffyStrToMillis(m.group("userClockTicks")),
+                                jiffyStrToMillis(m.group("niceClockTicks")),
+                                jiffyStrToMillis(m.group("sysClockTicks")),
+                                jiffyStrToMillis(m.group("idleClockTicks")),
+                                jiffyStrToMillis(m.group("iowaitClockTicks")),
+                                jiffyStrToMillis(m.group("irqClockTicks")),
+                                jiffyStrToMillis(m.group("softirqClockTicks")),
+                                jiffyStrToMillis(m.group("stealClockTicks")),
+                                jiffyStrToMillis(m.group("guestClockTicks")),
+                                jiffyStrToMillis(m.group("guestNiceClockTicks"))));
+            }
+        } catch (Exception e) {
+            Slogf.e(TAG, e, "Failed to read cpu usage stats from %s",
+                    mProcStatFile.getAbsolutePath());
+        }
+        return cpuUsageStats;
+    }
+
+    private static long jiffyStrToMillis(String jiffyStr) {
+        return Long.parseLong(Objects.requireNonNull(jiffyStr)) * MILLIS_PER_JIFFY;
+    }
+
+    /** Contains information for each CPU core on the system. */
+    public static final class CpuInfo {
+        public final int cpuCore;
+        public final @CpusetCategory int cpusetCategories;
+        public final long curCpuFreqKHz;
+        public final long maxCpuFreqKHz;
+        public final CpuUsageStats latestCpuUsageStats;
+
+        CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, long curCpuFreqKHz,
+                long maxCpuFreqKHz, CpuUsageStats latestCpuUsageStats) {
+            this.cpuCore = cpuCore;
+            this.cpusetCategories = cpusetCategories;
+            this.curCpuFreqKHz = curCpuFreqKHz;
+            this.maxCpuFreqKHz = maxCpuFreqKHz;
+            this.latestCpuUsageStats = latestCpuUsageStats;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("CpuInfo{ cpuCore = ").append(cpuCore)
+                    .append(", cpusetCategories = ").append(cpusetCategories)
+                    .append(", curCpuFreqKHz = ").append(curCpuFreqKHz)
+                    .append(", maxCpuFreqKHz = ").append(maxCpuFreqKHz)
+                    .append(", latestCpuUsageStats = ").append(latestCpuUsageStats)
+                    .append(" }").toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof CpuInfo)) {
+                return false;
+            }
+            CpuInfo other = (CpuInfo) obj;
+            return cpuCore == other.cpuCore && cpusetCategories == other.cpusetCategories
+                    && curCpuFreqKHz == other.curCpuFreqKHz
+                    && maxCpuFreqKHz == other.maxCpuFreqKHz
+                    && latestCpuUsageStats.equals(other.latestCpuUsageStats);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(cpuCore, cpusetCategories, curCpuFreqKHz, maxCpuFreqKHz,
+                    latestCpuUsageStats);
+        }
+    }
+
+    /** CPU time spent in different modes. */
+    public static final class CpuUsageStats {
+        public final long userTimeMillis;
+        public final long niceTimeMillis;
+        public final long systemTimeMillis;
+        public final long idleTimeMillis;
+        public final long iowaitTimeMillis;
+        public final long irqTimeMillis;
+        public final long softirqTimeMillis;
+        public final long stealTimeMillis;
+        public final long guestTimeMillis;
+        public final long guestNiceTimeMillis;
+
+        public CpuUsageStats(long userTimeMillis, long niceTimeMillis, long systemTimeMillis,
+                long idleTimeMillis, long iowaitTimeMillis, long irqTimeMillis,
+                long softirqTimeMillis, long stealTimeMillis, long guestTimeMillis,
+                long guestNiceTimeMillis) {
+            this.userTimeMillis = userTimeMillis;
+            this.niceTimeMillis = niceTimeMillis;
+            this.systemTimeMillis = systemTimeMillis;
+            this.idleTimeMillis = idleTimeMillis;
+            this.iowaitTimeMillis = iowaitTimeMillis;
+            this.irqTimeMillis = irqTimeMillis;
+            this.softirqTimeMillis = softirqTimeMillis;
+            this.stealTimeMillis = stealTimeMillis;
+            this.guestTimeMillis = guestTimeMillis;
+            this.guestNiceTimeMillis = guestNiceTimeMillis;
+        }
+
+        public long getTotalTime() {
+            return userTimeMillis + niceTimeMillis + systemTimeMillis + idleTimeMillis
+                    + iowaitTimeMillis + irqTimeMillis + softirqTimeMillis + stealTimeMillis
+                    + guestTimeMillis + guestNiceTimeMillis;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("CpuUsageStats{ userTimeMillis = ")
+                    .append(userTimeMillis)
+                    .append(", niceTimeMillis = ").append(niceTimeMillis)
+                    .append(", systemTimeMillis = ").append(systemTimeMillis)
+                    .append(", idleTimeMillis = ").append(idleTimeMillis)
+                    .append(", iowaitTimeMillis = ").append(iowaitTimeMillis)
+                    .append(", irqTimeMillis = ").append(irqTimeMillis)
+                    .append(", softirqTimeMillis = ").append(softirqTimeMillis)
+                    .append(", stealTimeMillis = ").append(stealTimeMillis)
+                    .append(", guestTimeMillis = ").append(guestTimeMillis)
+                    .append(", guestNiceTimeMillis = ").append(guestNiceTimeMillis)
+                    .append(" }").toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof CpuUsageStats)) {
+                return false;
+            }
+            CpuUsageStats other = (CpuUsageStats) obj;
+            return userTimeMillis == other.userTimeMillis && niceTimeMillis == other.niceTimeMillis
+                    && systemTimeMillis == other.systemTimeMillis
+                    && idleTimeMillis == other.idleTimeMillis
+                    && iowaitTimeMillis == other.iowaitTimeMillis
+                    && irqTimeMillis == other.irqTimeMillis
+                    && softirqTimeMillis == other.softirqTimeMillis
+                    && stealTimeMillis == other.stealTimeMillis
+                    && guestTimeMillis == other.guestTimeMillis
+                    && guestNiceTimeMillis == other.guestNiceTimeMillis;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(userTimeMillis, niceTimeMillis, systemTimeMillis, idleTimeMillis,
+                    iowaitTimeMillis, irqTimeMillis, softirqTimeMillis, stealTimeMillis,
+                    guestTimeMillis,
+                    guestNiceTimeMillis);
+        }
+
+        CpuUsageStats delta(CpuUsageStats rhs) {
+            return new CpuUsageStats(diff(userTimeMillis, rhs.userTimeMillis),
+                    diff(niceTimeMillis, rhs.niceTimeMillis),
+                    diff(systemTimeMillis, rhs.systemTimeMillis),
+                    diff(idleTimeMillis, rhs.idleTimeMillis),
+                    diff(iowaitTimeMillis, rhs.iowaitTimeMillis),
+                    diff(irqTimeMillis, rhs.irqTimeMillis),
+                    diff(softirqTimeMillis, rhs.softirqTimeMillis),
+                    diff(stealTimeMillis, rhs.stealTimeMillis),
+                    diff(guestTimeMillis, rhs.guestTimeMillis),
+                    diff(guestNiceTimeMillis, rhs.guestNiceTimeMillis));
+        }
+
+        private static long diff(long lhs, long rhs) {
+            return lhs > rhs ? lhs - rhs : 0;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorInternal.java b/services/core/java/com/android/server/cpu/CpuMonitorInternal.java
new file mode 100644
index 0000000..849a20b
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuMonitorInternal.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import android.annotation.CallbackExecutor;
+
+import java.util.concurrent.Executor;
+
+/** CpuMonitorInternal hosts internal APIs to monitor CPU. */
+public abstract class CpuMonitorInternal {
+    /** Callback to get CPU availability change notifications. */
+    public interface CpuAvailabilityCallback {
+        /**
+         * Called when the CPU availability crosses the provided thresholds.
+         *
+         * <p>Called when the latest or past N-second (which will be specified in the
+         * {@link CpuAvailabilityInfo}) average CPU availability percent has crossed
+         * (either goes above or drop below) the {@link CpuAvailabilityMonitoringConfig#thresholds}
+         * since the last notification. Also called when a callback is added to the service.
+         *
+         * <p>The callback is called at the executor which is specified in
+         * {@link addCpuAvailabilityCallback} or at the service handler thread.
+         *
+         * @param info CPU availability information.
+         */
+        void onAvailabilityChanged(CpuAvailabilityInfo info);
+
+        /**
+         * Called when the CPU monitoring interval changes.
+         *
+         * <p>Also called when a callback is added to the service.
+         *
+         * @param intervalMilliseconds CPU monitoring interval in milliseconds.
+         */
+        void onMonitoringIntervalChanged(long intervalMilliseconds);
+    }
+
+    /**
+     * Adds the {@link CpuAvailabilityCallback} for the caller.
+     *
+     * <p>When the callback is added, the callback will be called to notify the current CPU
+     * availability and monitoring interval.
+     *
+     * <p>When the client needs to update the {@link config} for a previously added callback,
+     * the client has to remove the callback and add the callback with a new {@link config}.
+     *
+     * @param executor Executor to execute the callback. If an executor is not provided,
+     *                 the callback will be executed on the service handler thread.
+     * @param config CPU availability monitoring config.
+     * @param callback Callback implementing {@link CpuAvailabilityCallback}
+     * interface.
+     *
+     * @throws IllegalStateException if {@code callback} is already added.
+     */
+    public abstract void addCpuAvailabilityCallback(@CallbackExecutor Executor executor,
+            CpuAvailabilityMonitoringConfig config, CpuAvailabilityCallback callback);
+
+    /**
+     * Removes the {@link CpuAvailabilityCallback} for the caller.
+     *
+     * @param callback Callback implementing {@link CpuAvailabilityCallback}
+     * interface.
+     *
+     * @throws IllegalArgumentException if {@code callback} is not previously added.
+     */
+    public abstract void removeCpuAvailabilityCallback(CpuAvailabilityCallback callback);
+}
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorService.java b/services/core/java/com/android/server/cpu/CpuMonitorService.java
new file mode 100644
index 0000000..b0dfb84
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuMonitorService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+
+import android.content.Context;
+import android.os.Binder;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import com.android.server.utils.PriorityDump;
+import com.android.server.utils.Slogf;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** Service to monitor CPU availability and usage. */
+public final class CpuMonitorService extends SystemService {
+    static final String TAG = CpuMonitorService.class.getSimpleName();
+    static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
+    // TODO(b/242722241): Make this a resource overlay property.
+    //  Maintain 3 monitoring intervals:
+    //  * One to poll very frequently when mCpuAvailabilityCallbackInfoByCallbacks are available and
+    //    CPU availability is above a threshold (such as at least 10% of CPU is available).
+    //  * One to poll less frequently when mCpuAvailabilityCallbackInfoByCallbacks are available
+    //    and CPU availability is below a threshold (such as less than 10% of CPU is available).
+    //  * One to poll very less frequently when no callbacks are available and the build is either
+    //    user-debug or eng. This will be useful for debugging in development environment.
+    static final int DEFAULT_CPU_MONITORING_INTERVAL_MILLISECONDS = 5_000;
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final ArrayMap<CpuMonitorInternal.CpuAvailabilityCallback, CpuAvailabilityCallbackInfo>
+            mCpuAvailabilityCallbackInfoByCallbacks = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private long mMonitoringIntervalMilliseconds = DEFAULT_CPU_MONITORING_INTERVAL_MILLISECONDS;
+
+    private final CpuMonitorInternal mLocalService = new CpuMonitorInternal() {
+        @Override
+        public void addCpuAvailabilityCallback(Executor executor,
+                CpuAvailabilityMonitoringConfig config, CpuAvailabilityCallback callback) {
+            Objects.requireNonNull(callback, "Callback must be non-null");
+            Objects.requireNonNull(config, "Config must be non-null");
+            synchronized (mLock) {
+                if (mCpuAvailabilityCallbackInfoByCallbacks.containsKey(callback)) {
+                    Slogf.i(TAG, "Overwriting the existing CpuAvailabilityCallback %s",
+                            mCpuAvailabilityCallbackInfoByCallbacks.get(callback));
+                    // TODO(b/242722241): Overwrite any internal cache (will be added in future CLs)
+                    //  that maps callbacks based on the CPU availability thresholds.
+                }
+                CpuAvailabilityCallbackInfo info = new CpuAvailabilityCallbackInfo(config,
+                        executor);
+                mCpuAvailabilityCallbackInfoByCallbacks.put(callback, info);
+                if (DEBUG) {
+                    Slogf.d(TAG, "Added a CPU availability callback: %s", info);
+                }
+            }
+            // TODO(b/242722241):
+            //  * On the executor or on the handler thread, call the callback with the latest CPU
+            //    availability info and monitoring interval.
+            //  * Monitor the CPU stats more frequently when the first callback is added.
+        }
+
+        @Override
+        public void removeCpuAvailabilityCallback(CpuAvailabilityCallback callback) {
+            synchronized (mLock) {
+                if (!mCpuAvailabilityCallbackInfoByCallbacks.containsKey(callback)) {
+                    Slogf.i(TAG, "CpuAvailabilityCallback was not previously added."
+                            + " Ignoring the remove request");
+                    return;
+                }
+                CpuAvailabilityCallbackInfo info =
+                        mCpuAvailabilityCallbackInfoByCallbacks.remove(callback);
+                if (DEBUG) {
+                    Slogf.d(TAG, "Removed a CPU availability callback: %s", info);
+                }
+            }
+            // TODO(b/242722241): Increase CPU monitoring interval when all callbacks are removed.
+        }
+    };
+
+    public CpuMonitorService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        publishLocalService(CpuMonitorInternal.class, mLocalService);
+        publishBinderService("cpu_monitor", new CpuMonitorBinder(), /* allowIsolated= */ false,
+                DUMP_FLAG_PRIORITY_CRITICAL);
+    }
+
+    private void doDump(IndentingPrintWriter writer) {
+        writer.printf("*%s*\n", getClass().getSimpleName());
+        writer.increaseIndent();
+        synchronized (mLock) {
+            writer.printf("CPU monitoring interval: %d ms\n", mMonitoringIntervalMilliseconds);
+            if (!mCpuAvailabilityCallbackInfoByCallbacks.isEmpty()) {
+                writer.println("CPU availability change callbacks:");
+                writer.increaseIndent();
+                for (int i = 0; i < mCpuAvailabilityCallbackInfoByCallbacks.size(); i++) {
+                    writer.printf("%s: %s\n", mCpuAvailabilityCallbackInfoByCallbacks.keyAt(i),
+                            mCpuAvailabilityCallbackInfoByCallbacks.valueAt(i));
+                }
+                writer.decreaseIndent();
+            }
+        }
+        // TODO(b/242722241): Print the recent past CPU stats.
+        writer.decreaseIndent();
+    }
+
+    private static final class CpuAvailabilityCallbackInfo {
+        public final CpuAvailabilityMonitoringConfig config;
+        public final Executor executor;
+
+        CpuAvailabilityCallbackInfo(CpuAvailabilityMonitoringConfig config,
+                Executor executor) {
+            this.config = config;
+            this.executor = executor;
+        }
+
+        @Override
+        public String toString() {
+            return "CpuAvailabilityCallbackInfo{" + "config=" + config + ", mExecutor=" + executor
+                    + '}';
+        }
+    }
+
+    private final class CpuMonitorBinder extends Binder {
+        private final PriorityDump.PriorityDumper mPriorityDumper =
+                new PriorityDump.PriorityDumper() {
+                    @Override
+                    public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
+                            boolean asProto) {
+                        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)
+                                || asProto) {
+                            return;
+                        }
+                        try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw)) {
+                            doDump(ipw);
+                        }
+                    }
+                };
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            PriorityDump.dump(mPriorityDumper, fd, pw, args);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 523a2dc..c1e9526 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1941,8 +1941,8 @@
     }
 
     private void setProxSensorUnspecified() {
-        mProximitySensor.name = "";
-        mProximitySensor.type = "";
+        mProximitySensor.name = null;
+        mProximitySensor.type = null;
     }
 
     private void loadProxSensorFromDdc(DisplayConfiguration config) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c5cb08d..1d04f2e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1573,7 +1573,7 @@
             mSyncRoot.notifyAll();
         }
 
-        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+        sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
 
         Runnable work = updateDisplayStateLocked(device);
         if (work != null) {
@@ -1592,7 +1592,7 @@
         // We don't bother invalidating the display info caches here because any changes to the
         // display info will trigger a cache invalidation inside of LogicalDisplay before we hit
         // this point.
-        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+        sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
         scheduleTraversalLocked(false);
         mPersistentDataStore.saveIfNeeded();
 
@@ -1622,7 +1622,7 @@
         mDisplayStates.delete(displayId);
         mDisplayBrightnesses.delete(displayId);
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
-        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+        sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
         scheduleTraversalLocked(false);
 
         if (mDisplayWindowPolicyControllers.contains(displayId)) {
@@ -1638,23 +1638,13 @@
     }
 
     private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
-        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-        final Runnable work = updateDisplayStateLocked(device);
-        if (work != null) {
-            mHandler.post(work);
-        }
-        final int displayId = display.getDisplayIdLocked();
+        handleLogicalDisplayChangedLocked(display);
 
+        final int displayId = display.getDisplayIdLocked();
         if (displayId == Display.DEFAULT_DISPLAY) {
             notifyDefaultDisplayDeviceUpdated(display);
         }
-        DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
-        if (dpc != null) {
-            dpc.onDisplayChanged();
-        }
-        mPersistentDataStore.saveIfNeeded();
         mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
-        handleLogicalDisplayChangedLocked(display);
     }
 
     private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) {
@@ -1666,7 +1656,7 @@
         final int displayId = display.getDisplayIdLocked();
         final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            dpc.onDeviceStateTransition();
+            dpc.onDisplayChanged();
         }
     }
 
@@ -2366,9 +2356,13 @@
         }
     }
 
-    private void sendDisplayEventLocked(int displayId, @DisplayEvent int event) {
-        Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
-        mHandler.sendMessage(msg);
+    private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) {
+        // Only send updates outside of DisplayManagerService for enabled displays
+        if (display.isEnabledLocked()) {
+            int displayId = display.getDisplayIdLocked();
+            Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+            mHandler.sendMessage(msg);
+        }
     }
 
     private void sendDisplayGroupEvent(int groupId, int event) {
@@ -2653,8 +2647,7 @@
     }
 
     private void handleBrightnessChange(LogicalDisplay display) {
-        sendDisplayEventLocked(display.getDisplayIdLocked(),
-                DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
+        sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
     }
 
     private DisplayDevice getDeviceForDisplayLocked(int displayId) {
@@ -2871,12 +2864,12 @@
          * Returns the list of all display ids.
          */
         @Override // Binder call
-        public int[] getDisplayIds() {
+        public int[] getDisplayIds(boolean includeDisabled) {
             final int callingUid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid, includeDisabled);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -3367,6 +3360,11 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
+                    LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(
+                            displayId, /* includeDisabled= */ false);
+                    if (display == null || !display.isEnabledLocked()) {
+                        return null;
+                    }
                     DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         return dpc.getBrightnessInfo();
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 306b8cf..1c510cc 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
 import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
 import static android.os.PowerManager.BRIGHTNESS_INVALID;
 
@@ -48,7 +49,7 @@
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
 import android.provider.Settings;
-import android.sysprop.DisplayProperties;
+import android.sysprop.SurfaceFlingerProperties;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
@@ -137,8 +138,7 @@
 
     private boolean mAlwaysRespectAppRequest;
 
-    // TODO(b/241447632): remove the flag once SF changes are ready
-    private final boolean mRenderFrameRateIsPhysicalRefreshRate;
+    private final boolean mSupportsFrameRateOverride;
 
     /**
      * The allowed refresh rate switching type. This is used by SurfaceFlinger.
@@ -175,7 +175,7 @@
         mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
                 mDeviceConfigDisplaySettings);
         mAlwaysRespectAppRequest = false;
-        mRenderFrameRateIsPhysicalRefreshRate = injector.renderFrameRateIsPhysicalRefreshRate();
+        mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
     }
 
     /**
@@ -237,21 +237,6 @@
             }
         }
 
-        if (mRenderFrameRateIsPhysicalRefreshRate) {
-            for (int i = 0; i < votes.size(); i++) {
-
-                Vote vote = votes.valueAt(i);
-                vote.refreshRateRanges.physical.min = Math.max(vote.refreshRateRanges.physical.min,
-                        vote.refreshRateRanges.render.min);
-                vote.refreshRateRanges.physical.max = Math.min(vote.refreshRateRanges.physical.max,
-                        vote.refreshRateRanges.render.max);
-                vote.refreshRateRanges.render.min = Math.max(vote.refreshRateRanges.physical.min,
-                        vote.refreshRateRanges.render.min);
-                vote.refreshRateRanges.render.max = Math.min(vote.refreshRateRanges.physical.max,
-                        vote.refreshRateRanges.render.max);
-            }
-        }
-
         return votes;
     }
 
@@ -540,8 +525,7 @@
                 }
             }
 
-            if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
-                    || mRenderFrameRateIsPhysicalRefreshRate) {
+            if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
                 primarySummary.minRenderFrameRate = primarySummary.minPhysicalRefreshRate;
                 primarySummary.maxRenderFrameRate = primarySummary.maxPhysicalRefreshRate;
                 appRequestSummary.minRenderFrameRate = appRequestSummary.minPhysicalRefreshRate;
@@ -611,6 +595,22 @@
                 continue;
             }
 
+            // The physical refresh rate must be in the render frame rate range, unless
+            // frame rate override is supported.
+            if (!mSupportsFrameRateOverride) {
+                if (physicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)
+                        || physicalRefreshRate > (summary.maxRenderFrameRate + FLOAT_TOLERANCE)) {
+                    if (mLoggingEnabled) {
+                        Slog.w(TAG, "Discarding mode " + mode.getModeId()
+                                + ", outside render rate bounds"
+                                + ": minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
+                                + ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
+                                + ", modeRefreshRate=" + physicalRefreshRate);
+                    }
+                    continue;
+                }
+            }
+
             // Check whether the render frame rate range is achievable by the mode's physical
             // refresh rate, meaning that if a divisor of the physical refresh rate is in range
             // of the render frame rate.
@@ -1640,7 +1640,7 @@
             SparseArray<Display.Mode[]> modes = new SparseArray<>();
             SparseArray<Display.Mode> defaultModes = new SparseArray<>();
             DisplayInfo info = new DisplayInfo();
-            Display[] displays = dm.getDisplays();
+            Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
             for (Display d : displays) {
                 final int displayId = d.getDisplayId();
                 d.getDisplayInfo(info);
@@ -2517,7 +2517,8 @@
             sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
 
             synchronized (mSensorObserverLock) {
-                for (Display d : mDisplayManager.getDisplays()) {
+                for (Display d : mDisplayManager.getDisplays(
+                        DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
                     mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
                 }
             }
@@ -2528,7 +2529,8 @@
         }
 
         private void recalculateVotesLocked() {
-            final Display[] displays = mDisplayManager.getDisplays();
+            final Display[] displays = mDisplayManager.getDisplays(
+                    DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
             for (Display d : displays) {
                 int displayId = d.getDisplayId();
                 Vote vote = null;
@@ -2976,7 +2978,7 @@
 
         IThermalService getThermalService();
 
-        boolean renderFrameRateIsPhysicalRefreshRate();
+        boolean supportsFrameRateOverride();
     }
 
     @VisibleForTesting
@@ -3031,9 +3033,11 @@
         }
 
         @Override
-        public boolean renderFrameRateIsPhysicalRefreshRate() {
-            return DisplayProperties
-                    .debug_render_frame_rate_is_physical_refresh_rate().orElse(true);
+        public boolean supportsFrameRateOverride() {
+            return SurfaceFlingerProperties.enable_frame_rate_override().orElse(false)
+                            && !SurfaceFlingerProperties.frame_rate_override_for_native_rates()
+                                    .orElse(true)
+                            && SurfaceFlingerProperties.frame_rate_override_global().orElse(false);
         }
 
         private DisplayManager getDisplayManager() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d6f0fd0..8124500 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -497,6 +497,9 @@
     private final String mSuspendBlockerIdProxNegative;
     private final String mSuspendBlockerIdProxDebounce;
 
+    private boolean mIsEnabled;
+    private boolean mIsInTransition;
+
     /**
      * Creates the display power controller.
      */
@@ -520,6 +523,8 @@
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
         mDisplayStatsId = mUniqueDisplayId.hashCode();
+        mIsEnabled = logicalDisplay.isEnabledLocked();
+        mIsInTransition = logicalDisplay.isInTransitionLocked();
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
         mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
@@ -807,29 +812,36 @@
         final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
         final IBinder token = device.getDisplayTokenLocked();
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+        final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
+        final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
         mHandler.post(() -> {
+            boolean changed = false;
             if (mDisplayDevice != device) {
+                changed = true;
                 mDisplayDevice = device;
                 mUniqueDisplayId = uniqueId;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
                 loadFromDisplayDeviceConfig(token, info);
+
+                // Since the underlying display-device changed, we really don't know the
+                // last command that was sent to change it's state. Lets assume it is unknown so
+                // that we trigger a change immediately.
+                mPowerState.resetScreenState();
+            }
+            if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
+                changed = true;
+                mIsEnabled = isEnabled;
+                mIsInTransition = isInTransition;
+            }
+
+            if (changed) {
                 updatePowerState();
             }
         });
     }
 
     /**
-     * Called when the displays are preparing to transition from one device state to another.
-     * This process involves turning off some displays so we need updatePowerState() to run and
-     * calculate the new state.
-     */
-    @Override
-    public void onDeviceStateTransition() {
-        sendUpdatePowerState();
-    }
-
-    /**
      * Unregisters all listeners and interrupts all running threads; halting future work.
      *
      * This method should be called when the DisplayPowerController is no longer in use; i.e. when
@@ -1316,8 +1328,8 @@
             mIgnoreProximityUntilChanged = false;
         }
 
-        if (!mLogicalDisplay.isEnabled()
-                || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+        if (!mIsEnabled
+                || mIsInTransition
                 || mScreenOffBecauseOfProximity) {
             state = Display.STATE_OFF;
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 1f58a1c..9a594e8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -422,6 +422,8 @@
 
     private DisplayDeviceConfig mDisplayDeviceConfig;
 
+    private boolean mIsEnabled;
+    private boolean mIsInTransition;
     /**
      * Creates the display power controller.
      */
@@ -439,6 +441,8 @@
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
                 .getDisplayDeviceConfig();
+        mIsEnabled = logicalDisplay.isEnabledLocked();
+        mIsInTransition = logicalDisplay.isInTransitionLocked();
         mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
         mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
@@ -721,30 +725,37 @@
         final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
         final IBinder token = device.getDisplayTokenLocked();
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+        final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
+        final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
         mHandler.post(() -> {
+            boolean changed = false;
             if (mDisplayDevice != device) {
+                changed = true;
                 mDisplayDevice = device;
                 mUniqueDisplayId = uniqueId;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
                 loadFromDisplayDeviceConfig(token, info);
                 mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
+
+                // Since the underlying display-device changed, we really don't know the
+                // last command that was sent to change it's state. Lets assume it is unknown so
+                // that we trigger a change immediately.
+                mPowerState.resetScreenState();
+            }
+            if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
+                changed = true;
+                mIsEnabled = isEnabled;
+                mIsInTransition = isInTransition;
+            }
+
+            if (changed) {
                 updatePowerState();
             }
         });
     }
 
     /**
-     * Called when the displays are preparing to transition from one device state to another.
-     * This process involves turning off some displays so we need updatePowerState() to run and
-     * calculate the new state.
-     */
-    @Override
-    public void onDeviceStateTransition() {
-        sendUpdatePowerState();
-    }
-
-    /**
      * Unregisters all listeners and interrupts all running threads; halting future work.
      *
      * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
@@ -1165,8 +1176,8 @@
 
         mDisplayPowerProximityStateController.updateProximityState(mPowerRequest, state);
 
-        if (!mLogicalDisplay.isEnabled()
-                || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+        if (!mIsEnabled
+                || mIsInTransition
                 || mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
             state = Display.STATE_OFF;
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 6677f35..46f1343 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -45,11 +45,6 @@
     void stop();
 
     /**
-     * Used to manage the displays preparing to transition from one device state to another.
-     */
-    void onDeviceStateTransition();
-
-    /**
      * Used to update the display's BrightnessConfiguration
      * @param config The new BrightnessConfiguration
      */
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 2f22d33..f650b11 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -145,7 +145,7 @@
     public void setScreenState(int state) {
         if (mScreenState != state) {
             if (DEBUG) {
-                Slog.d(TAG, "setScreenState: state=" + state);
+                Slog.w(TAG, "setScreenState: state=" + Display.stateToString(state));
             }
 
             mScreenState = state;
@@ -339,6 +339,15 @@
         if (mColorFade != null) mColorFade.dump(pw);
     }
 
+    /**
+     * Resets the screen state to unknown. Useful when the underlying display-device changes for the
+     * LogicalDisplay and we do not know the last state that was sent to it.
+     */
+    void resetScreenState() {
+        mScreenState = Display.STATE_UNKNOWN;
+        mScreenReady = false;
+    }
+
     private void scheduleScreenUpdate() {
         if (!mScreenUpdatePending) {
             mScreenUpdatePending = true;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 28bdce3..8dd169bf 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Point;
@@ -68,33 +67,6 @@
 final class LogicalDisplay {
     private static final String TAG = "LogicalDisplay";
 
-    /**
-     * Phase indicating the logical display's existence is hidden from the rest of the framework.
-     * This can happen if the current layout has specifically requested to keep this display
-     * disabled.
-     */
-    static final int DISPLAY_PHASE_DISABLED = -1;
-
-    /**
-     * Phase indicating that the logical display is going through a layout transition.
-     * When in this phase, other systems can choose to special case power-state handling of a
-     * display that might be in a transition.
-     */
-    static final int DISPLAY_PHASE_LAYOUT_TRANSITION = 0;
-
-    /**
-     * The display is exposed to the rest of the system and its power state is determined by a
-     * power-request from PowerManager.
-     */
-    static final int DISPLAY_PHASE_ENABLED = 1;
-
-    @IntDef(prefix = {"DISPLAY_PHASE" }, value = {
-        DISPLAY_PHASE_DISABLED,
-        DISPLAY_PHASE_LAYOUT_TRANSITION,
-        DISPLAY_PHASE_ENABLED
-    })
-    @interface DisplayPhase {}
-
     // The layer stack we use when the display has been blanked to prevent any
     // of its content from appearing.
     private static final int BLANK_LAYER_STACK = -1;
@@ -159,14 +131,6 @@
     private final Rect mTempDisplayRect = new Rect();
 
     /**
-     * Indicates the current phase of the display. Generally, phases supersede any
-     * requests from PowerManager in DPC's calculation for the display state. Only when the
-     * phase is ENABLED does PowerManager's request for the display take effect.
-     */
-    @DisplayPhase
-    private int mPhase = DISPLAY_PHASE_ENABLED;
-
-    /**
      * The UID mappings for refresh rate override
      */
     private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides;
@@ -181,12 +145,22 @@
      */
     private final SparseArray<Float> mTempFrameRateOverride;
 
+    // Indicates the display is enabled (allowed to be ON).
+    private boolean mIsEnabled;
+
+    // Indicates the display is part of a transition from one device-state ({@link
+    // DeviceStateManager}) to another. Being a "part" of a transition means that either
+    // the {@link mIsEnabled} is changing, or the underlying mPrimiaryDisplayDevice is changing.
+    private boolean mIsInTransition;
+
     public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
         mDisplayId = displayId;
         mLayerStack = layerStack;
         mPrimaryDisplayDevice = primaryDisplayDevice;
         mPendingFrameRateOverrideUids = new ArraySet<>();
         mTempFrameRateOverride = new SparseArray<>();
+        mIsEnabled = true;
+        mIsInTransition = false;
     }
 
     /**
@@ -531,7 +505,7 @@
         // Prevent displays that are disabled from receiving input.
         // TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
         device.setDisplayFlagsLocked(t,
-                (isEnabled() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE)
+                (isEnabledLocked() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE)
                         ? SurfaceControl.DISPLAY_RECEIVES_INPUT
                         : 0);
 
@@ -773,32 +747,45 @@
         return old;
     }
 
-    public void setPhase(@DisplayPhase int phase) {
-        mPhase = phase;
-    }
-
-    /**
-     * Returns the currently set phase for this LogicalDisplay. Phases are used when transitioning
-     * from one device state to another. {@see LogicalDisplayMapper}.
-     */
-    @DisplayPhase
-    public int getPhase() {
-        return mPhase;
-    }
-
     /**
      * @return {@code true} if the LogicalDisplay is enabled or {@code false}
      * if disabled indicating that the display should be hidden from the rest of the apps and
      * framework.
      */
-    public boolean isEnabled() {
-        // DISPLAY_PHASE_LAYOUT_TRANSITION is still considered an 'enabled' phase.
-        return mPhase == DISPLAY_PHASE_ENABLED || mPhase == DISPLAY_PHASE_LAYOUT_TRANSITION;
+    public boolean isEnabledLocked() {
+        return mIsEnabled;
+    }
+
+    /**
+     * Sets the display as enabled.
+     *
+     * @param enable True if enabled, false otherwise.
+     */
+    public void setEnabledLocked(boolean enabled) {
+        mIsEnabled = enabled;
+    }
+
+    /**
+     * @return {@code true} if the LogicalDisplay is in a transition phase. This is used to indicate
+     * that we are getting ready to swap the underlying display-device and the display should be
+     * rendered appropriately to reduce jank.
+     */
+    public boolean isInTransitionLocked() {
+        return mIsInTransition;
+    }
+
+    /**
+     * Sets the transition phase.
+     * @param isInTransition True if it display is in transition.
+     */
+    public void setIsInTransitionLocked(boolean isInTransition) {
+        mIsInTransition = isInTransition;
     }
 
     public void dumpLocked(PrintWriter pw) {
         pw.println("mDisplayId=" + mDisplayId);
-        pw.println("mPhase=" + mPhase);
+        pw.println("mIsEnabled=" + mIsEnabled);
+        pw.println("mIsInTransition=" + mIsInTransition);
         pw.println("mLayerStack=" + mLayerStack);
         pw.println("mHasContent=" + mHasContent);
         pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}");
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index cb97e28..66073c2 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -40,7 +40,6 @@
 import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.LogicalDisplay.DisplayPhase;
 import com.android.server.display.layout.Layout;
 
 import java.io.PrintWriter;
@@ -180,6 +179,12 @@
     LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
             @NonNull Handler handler) {
+        this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap());
+    }
+
+    LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
+            @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
+            @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) {
         mSyncRoot = syncRoot;
         mPowerManager = context.getSystemService(PowerManager.class);
         mInteractive = mPowerManager.isInteractive();
@@ -194,7 +199,7 @@
         mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
                 com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
         mDisplayDeviceRepo.addListener(this);
-        mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
+        mDeviceStateToLayoutMap = deviceStateToLayoutMap;
     }
 
     @Override
@@ -231,10 +236,22 @@
     }
 
     public LogicalDisplay getDisplayLocked(int displayId) {
-        return mLogicalDisplays.get(displayId);
+        return getDisplayLocked(displayId, /* includeDisabled= */ true);
+    }
+
+    public LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
+        LogicalDisplay display = mLogicalDisplays.get(displayId);
+        if (display == null || display.isEnabledLocked() || includeDisabled) {
+            return display;
+        }
+        return null;
     }
 
     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
+        return getDisplayLocked(device, /* includeDisabled= */ true);
+    }
+
+    public LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
         if (device == null) {
             return null;
         }
@@ -242,21 +259,26 @@
         for (int i = 0; i < count; i++) {
             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
             if (display.getPrimaryDisplayDeviceLocked() == device) {
-                return display;
+                if (display.isEnabledLocked() || includeDisabled) {
+                    return display;
+                }
+                return null;
             }
         }
         return null;
     }
 
-    public int[] getDisplayIdsLocked(int callingUid) {
+    public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabled) {
         final int count = mLogicalDisplays.size();
         int[] displayIds = new int[count];
         int n = 0;
         for (int i = 0; i < count; i++) {
             LogicalDisplay display = mLogicalDisplays.valueAt(i);
-            DisplayInfo info = display.getDisplayInfoLocked();
-            if (info.hasAccess(callingUid)) {
-                displayIds[n++] = mLogicalDisplays.keyAt(i);
+            if (display.isEnabledLocked() || includeDisabled) {
+                DisplayInfo info = display.getDisplayInfoLocked();
+                if (info.hasAccess(callingUid)) {
+                    displayIds[n++] = mLogicalDisplays.keyAt(i);
+                }
             }
         }
         if (n != count) {
@@ -390,14 +412,12 @@
 
     void setDeviceStateLocked(int state, boolean isOverrideActive) {
         Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
-                + ", interactive=" + mInteractive);
+                + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
         // As part of a state transition, we may need to turn off some displays temporarily so that
         // the transition is smooth. Plus, on some devices, only one internal displays can be
-        // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be
+        // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
         // temporarily turned off.
-        if (mDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) {
-            resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
-        }
+        resetLayoutLocked(mDeviceState, state, /* transitionValue= */ true);
         mPendingDeviceState = state;
         final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
                 mInteractive, mBootCompleted);
@@ -507,7 +527,7 @@
         final int count = mLogicalDisplays.size();
         for (int i = 0; i < count; i++) {
             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
-            if (display.getPhase() != LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
+            if (!display.isInTransitionLocked()) {
                 continue;
             }
 
@@ -523,7 +543,7 @@
     }
 
     private void transitionToPendingStateLocked() {
-        resetLayoutLocked(mDeviceState, mPendingDeviceState, LogicalDisplay.DISPLAY_PHASE_ENABLED);
+        resetLayoutLocked(mDeviceState, mPendingDeviceState, /* transitionValue= */ false);
         mDeviceState = mPendingDeviceState;
         mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
         applyLayoutLocked();
@@ -838,17 +858,17 @@
 
     /**
      * Goes through all the displays used in the layouts for the specified {@code fromState} and
-     * {@code toState} and applies the specified {@code phase}. When a new layout is requested, we
-     * put the displays that will change into a transitional phase so that they can all be turned
-     * OFF. Once all are confirmed OFF, then this method gets called again to reset the phase to
-     * normal operation. This helps to ensure that all display-OFF requests are made before
+     * {@code toState} and un/marks them for transition. When a new layout is requested, we
+     * mark the displays that will change into a transitional phase so that they can all be turned
+     * OFF. Once all are confirmed OFF, then this method gets called again to reset transition
+     * marker. This helps to ensure that all display-OFF requests are made before
      * display-ON which in turn hides any resizing-jank windows might incur when switching displays.
      *
      * @param fromState The state we are switching from.
      * @param toState The state we are switching to.
-     * @param phase The new phase to apply to the displays.
+     * @param transitionValue The value to mark the transition state: true == transitioning.
      */
-    private void resetLayoutLocked(int fromState, int toState, @DisplayPhase int phase) {
+    private void resetLayoutLocked(int fromState, int toState, boolean transitionValue) {
         final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState);
         final Layout toLayout = mDeviceStateToLayoutMap.get(toState);
 
@@ -866,12 +886,16 @@
             // new layout.
             final DisplayAddress address = device.getDisplayDeviceInfoLocked().address;
 
-            // Virtual displays do not have addresses.
+            // Virtual displays do not have addresses, so account for nulls.
             final Layout.Display fromDisplay =
                     address != null ? fromLayout.getByAddress(address) : null;
             final Layout.Display toDisplay =
                     address != null ? toLayout.getByAddress(address) : null;
 
+            // If the display is in one of the layouts but not the other, then the content will
+            // change, so in this case we also want to blank the displays to avoid jank.
+            final boolean displayNotInBothLayouts = (fromDisplay == null) != (toDisplay == null);
+
             // If a layout doesn't mention a display-device at all, then the display-device defaults
             // to enabled. This is why we treat null as "enabled" in the code below.
             final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled();
@@ -886,16 +910,23 @@
             // 3) It's enabled, but it's mapped to a new logical display ID. To the user this
             //    would look like apps moving from one screen to another since task-stacks stay
             //    with the logical display [ID].
+            // 4) It's in one layout but not the other, so the content will change.
             final boolean isTransitioning =
-                    (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION)
+                    logicalDisplay.isInTransitionLocked()
                     || (wasEnabled != willBeEnabled)
-                    || deviceHasNewLogicalDisplayId;
+                    || deviceHasNewLogicalDisplayId
+                    || displayNotInBothLayouts;
 
             if (isTransitioning) {
-                setDisplayPhase(logicalDisplay, phase);
-                if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
-                    mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
+                if (transitionValue != logicalDisplay.isInTransitionLocked()) {
+                    Slog.i(TAG, "Set isInTransition on display " + displayId + ": "
+                            + transitionValue);
                 }
+                // This will either mark the display as "transitioning" if we are starting to change
+                // the device state, or remove the transitioning marker if the state change is
+                // ending.
+                logicalDisplay.setIsInTransitionLocked(transitionValue);
+                mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
             }
         }
     }
@@ -940,9 +971,7 @@
                 newDisplay.swapDisplaysLocked(oldDisplay);
             }
 
-            if (!displayLayout.isEnabled()) {
-                setDisplayPhase(newDisplay, LogicalDisplay.DISPLAY_PHASE_DISABLED);
-            }
+            setEnabledLocked(newDisplay, displayLayout.isEnabled());
         }
 
     }
@@ -961,23 +990,25 @@
         final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
         display.updateLocked(mDisplayDeviceRepo);
         mLogicalDisplays.put(displayId, display);
-        setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
         return display;
     }
 
-    private void setDisplayPhase(LogicalDisplay display, @DisplayPhase int phase) {
+    private void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
         final int displayId = display.getDisplayIdLocked();
         final DisplayInfo info = display.getDisplayInfoLocked();
 
         final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
                 && (info.type != Display.TYPE_INTERNAL);
-        if (phase != LogicalDisplay.DISPLAY_PHASE_DISABLED && disallowSecondaryDisplay) {
+        if (isEnabled && disallowSecondaryDisplay) {
             Slog.i(TAG, "Not creating a logical display for a secondary display because single"
                     + " display demo mode is enabled: " + display.getDisplayInfoLocked());
-            phase = LogicalDisplay.DISPLAY_PHASE_DISABLED;
+            isEnabled = false;
         }
 
-        display.setPhase(phase);
+        if (display.isEnabledLocked() != isEnabled) {
+            Slog.i(TAG, "SetEnabled on display " + displayId + ": " + isEnabled);
+            display.setEnabledLocked(isEnabled);
+        }
     }
 
     private int assignDisplayGroupIdLocked(
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index cb40b40..4924ad5 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -33,6 +33,9 @@
      */
     public static Sensor findSensor(SensorManager sensorManager, String sensorType,
             String sensorName, int fallbackType) {
+        if ("".equals(sensorName) && "".equals(sensorType)) {
+            return null;
+        }
         final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
         final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
         if (isNameSpecified || isTypeSpecified) {
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index fac001e..c2157a6 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -79,9 +79,10 @@
     // (requires restart)
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 1;
-    private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 2;
-    private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 3;
+    private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
+    private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
+    private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
+    private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
 
     private final Context mContext;
     private final NativeInputManagerService mNative;
@@ -121,10 +122,10 @@
         InputManager inputManager = Objects.requireNonNull(
                 mContext.getSystemService(InputManager.class));
         inputManager.registerInputDeviceListener(this, mHandler);
-        // Circle through all the already added input devices
-        for (int deviceId : inputManager.getInputDeviceIds()) {
-            onInputDeviceAdded(deviceId);
-        }
+
+        Message msg = Message.obtain(mHandler, MSG_UPDATE_EXISTING_DEVICES,
+                inputManager.getInputDeviceIds());
+        mHandler.sendMessage(msg);
     }
 
     @Override
@@ -682,6 +683,13 @@
 
     private boolean handleMessage(Message msg) {
         switch (msg.what) {
+            case MSG_UPDATE_EXISTING_DEVICES:
+                // Circle through all the already added input devices
+                // Need to do it on handler thread and not block IMS thread
+                for (int deviceId : (int[]) msg.obj) {
+                    onInputDeviceAdded(deviceId);
+                }
+                return true;
             case MSG_SWITCH_KEYBOARD_LAYOUT:
                 handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
                 return true;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 90245b5e..7f6c2d6 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -73,6 +73,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -147,7 +148,6 @@
     private final ScheduledThreadPoolExecutor mDailyMetricTimer =
             new ScheduledThreadPoolExecutor(1);
 
-
     // The period of the recurring time
     private static final int PERIOD_METRIC_QUERY_DAYS = 1;
 
@@ -363,11 +363,13 @@
      */
     private void initDefaultClientMap() {
         HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
-        for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+        for (Map.Entry<Integer, ContextHubInfo> entry: mContextHubIdToInfoMap.entrySet()) {
+            int contextHubId = entry.getKey();
+            ContextHubInfo contextHubInfo = entry.getValue();
+
             mLastRestartTimestampMap.put(contextHubId,
                     new AtomicLong(SystemClock.elapsedRealtimeNanos()));
 
-            ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
             IContextHubClient client = mClientManager.registerClient(
                     contextHubInfo, createDefaultClientCallback(contextHubId),
                     /* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
@@ -1133,6 +1135,26 @@
         mTransactionManager.addTransaction(transaction);
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+    /**
+     * Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
+     *
+     * @param hubInfo The Context Hub to query a list of nanoapps from.
+     * @return The list of 64-bit IDs of the preloaded nanoapps.
+     * @throws NullPointerException if hubInfo is null
+     */
+    @Override
+    public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
+        super.getPreloadedNanoAppIds_enforcePermission();
+        Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
+
+        long[] nanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
+        if (nanoappIds == null) {
+            return new long[0];
+        }
+        return nanoappIds;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1160,6 +1182,10 @@
         mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
 
         pw.println("");
+        pw.println("=================== PRELOADED NANOAPPS ====================");
+        dumpPreloadedNanoapps(pw);
+
+        pw.println("");
         pw.println("=================== CLIENTS ====================");
         pw.println(mClientManager);
 
@@ -1201,6 +1227,21 @@
         proto.flush();
     }
 
+    /**
+     * Dumps preloaded nanoapps to the console
+     */
+    private void dumpPreloadedNanoapps(PrintWriter pw) {
+        if (mContextHubWrapper == null) {
+            return;
+        }
+
+        long[] preloadedNanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
+        for (long preloadedNanoappId: preloadedNanoappIds) {
+            pw.print("ID: 0x");
+            pw.println(Long.toHexString(preloadedNanoappId));
+        }
+    }
+
     private void checkPermissions() {
         ContextHubServiceUtil.checkPermissions(mContext);
     }
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 48152b4..f55ae6e 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -359,6 +359,14 @@
     public abstract int queryNanoapps(int contextHubId) throws RemoteException;
 
     /**
+     * Provides the list of preloaded nanoapp IDs on the system. The output of this API must
+     * not change.
+     *
+     * @return The list of preloaded nanoapp IDs
+     */
+    public abstract long[] getPreloadedNanoappIds();
+
+    /**
      * Registers a callback with the Context Hub.
      *
      * @param contextHubId The ID of the Context Hub to register the callback with.
@@ -683,6 +691,20 @@
             }
         }
 
+        public long[] getPreloadedNanoappIds() {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return null;
+            }
+
+            try {
+                return hub.getPreloadedNanoappIds();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Exception while getting preloaded nanoapp IDs: " + e.getMessage());
+                return null;
+            }
+        }
+
         public void registerExistingCallback(int contextHubId) {
             android.hardware.contexthub.IContextHub hub = getHub();
             if (hub == null) {
@@ -863,6 +885,10 @@
                     mHub.queryApps(contextHubId));
         }
 
+        public long[] getPreloadedNanoappIds() {
+            return new long[0];
+        }
+
         public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
             mHidlCallbackMap.put(contextHubId,
                         new ContextHubWrapperHidlCallback(contextHubId, callback));
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index dcdb881..72ce38b 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -275,6 +275,10 @@
                 String.valueOf(mComponentType));
     }
 
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
     @ComponentType
     private static int getComponentType(PendingIntent pendingIntent) {
         if (pendingIntent.isBroadcast()) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 283640d..5d01aeb 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3923,13 +3923,20 @@
                 && !pkgSetting.getPathString().equals(parsedPackage.getPath());
         final boolean newPkgVersionGreater = pkgAlreadyExists
                 && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
+        final boolean newSharedUserSetting = pkgAlreadyExists
+                && (initialScanRequest.mOldSharedUserSetting
+                != initialScanRequest.mSharedUserSetting);
         final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
-                && newPkgChangedPaths && newPkgVersionGreater;
+                && newPkgChangedPaths && (newPkgVersionGreater || newSharedUserSetting);
         if (isSystemPkgBetter) {
             // The version of the application on /system is greater than the version on
             // /data. Switch back to the application on /system.
             // It's safe to assume the application on /system will correctly scan. If not,
             // there won't be a working copy of the application.
+            // Also, if the sharedUserSetting of the application on /system is different
+            // from the sharedUserSetting on /data, switch back to the application on /system.
+            // We should trust the sharedUserSetting on /system, even if the application
+            // version on /system is smaller than the version on /data.
             synchronized (mPm.mLock) {
                 // just remove the loaded entries from package lists
                 mPm.mPackages.remove(pkgSetting.getPackageName());
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index df132a9..0f920c6 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -435,8 +435,10 @@
     /** Removes a {@link UserVisibilityListener}. */
     public abstract void removeUserVisibilityListener(UserVisibilityListener listener);
 
-    /** TODO(b/244333150): temporary method until UserVisibilityMediator handles that logic */
-    public abstract void onUserVisibilityChanged(@UserIdInt int userId, boolean visible);
+    // TODO(b/242195409): remove this method if not needed anymore
+    /** Notify {@link UserVisibilityListener listeners} that the visibility of the
+     * {@link android.os.UserHandle#USER_SYSTEM} changed. */
+    public abstract void onSystemUserVisibilityChanged(boolean visible);
 
     /** Return the integer types of the given user IDs. Only used for reporting metrics to statsd.
      */
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 494f46c..3234e87 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -97,7 +97,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.Slog;
@@ -126,7 +125,6 @@
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
-import com.android.server.am.EventLogTags;
 import com.android.server.am.UserState;
 import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
 import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
@@ -512,10 +510,6 @@
     @GuardedBy("mUserLifecycleListeners")
     private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>();
 
-    // TODO(b/244333150): temporary array, should belong to UserVisibilityMediator
-    @GuardedBy("mUserVisibilityListeners")
-    private final ArrayList<UserVisibilityListener> mUserVisibilityListeners = new ArrayList<>();
-
     private final LockPatternUtils mLockPatternUtils;
 
     private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK =
@@ -6383,9 +6377,6 @@
         synchronized (mUserLifecycleListeners) {
             pw.println("  user lifecycle events: " + mUserLifecycleListeners.size());
         }
-        synchronized (mUserVisibilityListeners) {
-            pw.println("  user visibility events: " + mUserVisibilityListeners.size());
-        }
 
         // Dump UserTypes
         pw.println();
@@ -6961,31 +6952,17 @@
 
         @Override
         public void addUserVisibilityListener(UserVisibilityListener listener) {
-            synchronized (mUserVisibilityListeners) {
-                mUserVisibilityListeners.add(listener);
-            }
+            mUserVisibilityMediator.addListener(listener);
         }
 
         @Override
         public void removeUserVisibilityListener(UserVisibilityListener listener) {
-            synchronized (mUserVisibilityListeners) {
-                mUserVisibilityListeners.remove(listener);
-            }
+            mUserVisibilityMediator.removeListener(listener);
         }
 
         @Override
-        public void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
-            EventLog.writeEvent(EventLogTags.UM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
-            mHandler.post(() -> {
-                UserVisibilityListener[] listeners;
-                synchronized (mUserVisibilityListeners) {
-                    listeners = new UserVisibilityListener[mUserVisibilityListeners.size()];
-                    mUserVisibilityListeners.toArray(listeners);
-                }
-                for (UserVisibilityListener listener : listeners) {
-                    listener.onUserVisibilityChanged(userId, visible);
-                }
-            });
+        public void onSystemUserVisibilityChanged(boolean visible) {
+            mUserVisibilityMediator.onSystemUserVisibilityChanged(visible);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index 878855a..9b9ca10 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -32,6 +32,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Dumpable;
+import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.SparseIntArray;
@@ -40,6 +41,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.am.EventLogTags;
 import com.android.server.pm.UserManagerInternal.UserAssignmentResult;
 import com.android.server.pm.UserManagerInternal.UserVisibilityListener;
 import com.android.server.utils.Slogf;
@@ -68,6 +70,7 @@
 public final class UserVisibilityMediator implements Dumpable {
 
     private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
+    private static final boolean VERBOSE = false; // DO NOT SUBMIT WITH TRUE
 
     private static final String TAG = UserVisibilityMediator.class.getSimpleName();
 
@@ -381,8 +384,8 @@
     public boolean isUserVisible(@UserIdInt int userId) {
         // First check current foreground user and their profiles (on main display)
         if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
-            if (DBG) {
-                Slogf.d(TAG, "isUserVisible(%d): true to current user or profile", userId);
+            if (VERBOSE) {
+                Slogf.v(TAG, "isUserVisible(%d): true to current user or profile", userId);
             }
             return true;
         }
@@ -517,6 +520,14 @@
         }
     }
 
+    // TODO(b/242195409): remove this method if not needed anymore
+    /**
+     * Nofify all listeners that the system user visibility changed.
+     */
+    void onSystemUserVisibilityChanged(boolean visible) {
+        dispatchVisibilityChanged(mListeners, USER_SYSTEM, visible);
+    }
+
     /**
      * Nofify all listeners about the visibility changes from before / after a change of state.
      */
@@ -534,7 +545,7 @@
             Slogf.d(TAG,
                     "dispatchVisibilityChanged(): visibleUsersBefore=%s, visibleUsersAfter=%s, "
                     + "%d listeners (%s)", visibleUsersBefore, visibleUsersAfter, listeners.size(),
-                    mListeners);
+                    listeners);
         }
         for (int i = 0; i < visibleUsersBefore.size(); i++) {
             int userId = visibleUsersBefore.get(i);
@@ -552,13 +563,14 @@
 
     private void dispatchVisibilityChanged(CopyOnWriteArrayList<UserVisibilityListener> listeners,
             @UserIdInt int userId, boolean visible) {
+        EventLog.writeEvent(EventLogTags.UM_USER_VISIBILITY_CHANGED, userId, visible ? 1 : 0);
         if (DBG) {
             Slogf.d(TAG, "dispatchVisibilityChanged(%d -> %b): sending to %d listeners",
                     userId, visible, listeners.size());
         }
         for (int i = 0; i < mListeners.size(); i++) {
             UserVisibilityListener listener =  mListeners.get(i);
-            if (DBG) {
+            if (VERBOSE) {
                 Slogf.v(TAG, "dispatchVisibilityChanged(%d -> %b): sending to %s",
                         userId, visible, listener);
             }
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0bdd980..046db92 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -320,15 +320,13 @@
 
         switch (profileType) {
             case ArtManager.PROFILE_APPS :
-                return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+                return true;
             case ArtManager.PROFILE_BOOT_IMAGE:
                 // The device config property overrides the system property version.
                 boolean profileBootClassPath = SystemProperties.getBoolean(
                         "persist.device_config.runtime_native_boot.profilebootclasspath",
                         SystemProperties.getBoolean("dalvik.vm.profilebootclasspath", false));
-                return (Build.IS_USERDEBUG || Build.IS_ENG) &&
-                        SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) &&
-                        profileBootClassPath;
+                return (Build.IS_USERDEBUG || Build.IS_ENG) && profileBootClassPath;
             default:
                 throw new IllegalArgumentException("Invalid profile type:" + profileType);
         }
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 558202b..a3fa25d 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -533,7 +533,7 @@
             ai.metaData = null;
         }
         ai.applicationInfo = applicationInfo;
-        ai.targetDisplayCategory = a.getTargetDisplayCategory();
+        ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
         ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
         assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
         return ai;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
index e019215..1826f7a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -98,8 +98,8 @@
     boolean isSupportsSizeChanges();
 
     /**
-     * Gets the category of the target display this activity is supposed to run on.
+     * Gets the required category of the display this activity is supposed to run on.
      */
     @Nullable
-    String getTargetDisplayCategory();
+    String getRequiredDisplayCategory();
 }
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 278e547..68d5428 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -97,7 +97,7 @@
     private ActivityInfo.WindowLayout windowLayout;
 
     @Nullable
-    private String mTargetDisplayCategory;
+    private String mRequiredDisplayCategory;
 
     public ParsedActivityImpl(ParsedActivityImpl other) {
         super(other);
@@ -125,7 +125,7 @@
         this.colorMode = other.colorMode;
         this.windowLayout = other.windowLayout;
         this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
-        this.mTargetDisplayCategory = other.mTargetDisplayCategory;
+        this.mRequiredDisplayCategory = other.mRequiredDisplayCategory;
     }
 
     /**
@@ -193,7 +193,7 @@
         alias.requestedVrComponent = target.getRequestedVrComponent();
         alias.setDirectBootAware(target.isDirectBootAware());
         alias.setProcessName(target.getProcessName());
-        alias.setTargetDisplayCategory(target.getTargetDisplayCategory());
+        alias.setRequiredDisplayCategory(target.getRequiredDisplayCategory());
         return alias;
 
         // Not all attributes from the target ParsedActivity are copied to the alias.
@@ -321,7 +321,7 @@
             dest.writeBoolean(false);
         }
         sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
-        dest.writeString8(this.mTargetDisplayCategory);
+        dest.writeString8(this.mRequiredDisplayCategory);
     }
 
     public ParsedActivityImpl() {
@@ -356,7 +356,7 @@
             windowLayout = new ActivityInfo.WindowLayout(in);
         }
         this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
-        this.mTargetDisplayCategory = in.readString8();
+        this.mRequiredDisplayCategory = in.readString8();
     }
 
     @NonNull
@@ -414,7 +414,7 @@
             int rotationAnimation,
             int colorMode,
             @Nullable ActivityInfo.WindowLayout windowLayout,
-            @Nullable String targetDisplayCategory) {
+            @Nullable String requiredDisplayCategory) {
         this.theme = theme;
         this.uiOptions = uiOptions;
         this.targetActivity = targetActivity;
@@ -439,7 +439,7 @@
         this.rotationAnimation = rotationAnimation;
         this.colorMode = colorMode;
         this.windowLayout = windowLayout;
-        this.mTargetDisplayCategory = targetDisplayCategory;
+        this.mRequiredDisplayCategory = requiredDisplayCategory;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -560,8 +560,8 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable String getTargetDisplayCategory() {
-        return mTargetDisplayCategory;
+    public @Nullable String getRequiredDisplayCategory() {
+        return mRequiredDisplayCategory;
     }
 
     @DataClass.Generated.Member
@@ -691,16 +691,16 @@
     }
 
     @DataClass.Generated.Member
-    public @NonNull ParsedActivityImpl setTargetDisplayCategory(@NonNull String value) {
-        mTargetDisplayCategory = value;
+    public @NonNull ParsedActivityImpl setRequiredDisplayCategory(@NonNull String value) {
+        mRequiredDisplayCategory = value;
         return this;
     }
 
     @DataClass.Generated(
-            time = 1664805688714L,
+            time = 1669437519576L,
             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 mTargetDisplayCategory\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.server.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.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            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.server.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.server.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/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index 305062b..ea791e1 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -220,17 +220,17 @@
                 pkg.setVisibleToInstantApps(true);
             }
 
-            String targetDisplayCategory = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestActivity_targetDisplayCategory, 0);
+            String requiredDisplayCategory = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivity_requiredDisplayCategory, 0);
 
-            if (targetDisplayCategory != null
-                    && FrameworkParsingPackageUtils.validateName(targetDisplayCategory,
+            if (requiredDisplayCategory != null
+                    && FrameworkParsingPackageUtils.validateName(requiredDisplayCategory,
                     false /* requireSeparator */, false /* requireFilename */) != null) {
-                return input.error("targetDisplayCategory attribute can only consists of "
-                        + "alphanumeric characters, '_', and '.'");
+                return input.error("requiredDisplayCategory attribute can only consist "
+                        + "of alphanumeric characters, '_', and '.'");
             }
 
-            activity.setTargetDisplayCategory(targetDisplayCategory);
+            activity.setRequiredDisplayCategory(requiredDisplayCategory);
 
             return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                     false /*isAlias*/, visibleToEphemeral, input,
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 9953ca8..1358417 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -83,6 +83,9 @@
     @Nullable
     @GuardedBy("this")
     private Looper mLooper;
+    @Nullable
+    @GuardedBy("this")
+    private EnergyConsumer[] mEnergyConsumers = null;
 
     @VisibleForTesting
     static class Injector {
@@ -260,6 +263,15 @@
         }
     }
 
+    private EnergyConsumer[] getEnergyConsumerInfo() {
+        synchronized (this) {
+            if (mEnergyConsumers == null) {
+                mEnergyConsumers = getPowerStatsHal().getEnergyConsumerInfo();
+            }
+            return mEnergyConsumers;
+        }
+    }
+
     public PowerStatsService(Context context) {
         this(context, new Injector());
     }
@@ -327,7 +339,69 @@
 
     private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
             int[] energyConsumerIds) {
-        future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
+        EnergyConsumerResult[] results = getPowerStatsHal().getEnergyConsumed(energyConsumerIds);
+
+        // STOPSHIP(253292374): Remove once missing EnergyConsumer results issue is resolved.
+        EnergyConsumer[] energyConsumers = getEnergyConsumerInfo();
+        if (energyConsumers != null) {
+            final int expectedLength;
+            if (energyConsumerIds.length == 0) {
+                // Empty request is a request for all available EnergyConsumers.
+                expectedLength = energyConsumers.length;
+            } else {
+                expectedLength = energyConsumerIds.length;
+            }
+
+            if (results == null || expectedLength != results.length) {
+                // Mismatch in requested/received energy consumer data.
+                StringBuilder sb = new StringBuilder();
+                sb.append("Requested ids:");
+                if (energyConsumerIds.length == 0) {
+                    sb.append("ALL");
+                }
+                sb.append("[");
+                for (int i = 0; i < expectedLength; i++) {
+                    final int id = energyConsumerIds[i];
+                    sb.append(id);
+                    sb.append("(type:");
+                    sb.append(energyConsumers[id].type);
+                    sb.append(",ord:");
+                    sb.append(energyConsumers[id].ordinal);
+                    sb.append(",name:");
+                    sb.append(energyConsumers[id].name);
+                    sb.append(")");
+                    if (i != expectedLength - 1) {
+                        sb.append(", ");
+                    }
+                }
+                sb.append("]");
+
+                sb.append(", Received result ids:");
+                if (results == null) {
+                    sb.append("null");
+                } else {
+                    sb.append("[");
+                    final int resultLength = results.length;
+                    for (int i = 0; i < resultLength; i++) {
+                        final int id = results[i].id;
+                        sb.append(id);
+                        sb.append("(type:");
+                        sb.append(energyConsumers[id].type);
+                        sb.append(",ord:");
+                        sb.append(energyConsumers[id].ordinal);
+                        sb.append(",name:");
+                        sb.append(energyConsumers[id].name);
+                        sb.append(")");
+                        if (i != resultLength - 1) {
+                            sb.append(", ");
+                        }
+                    }
+                    sb.append("]");
+                }
+                Slog.wtf(TAG, "Missing result from getEnergyConsumedAsync call. " + sb);
+            }
+        }
+        future.complete(results);
     }
 
     private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future,
diff --git a/services/core/java/com/android/server/utils/EventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java
index 770cb72..4772bbf 100644
--- a/services/core/java/com/android/server/utils/EventLogger.java
+++ b/services/core/java/com/android/server/utils/EventLogger.java
@@ -36,8 +36,11 @@
  */
 public class EventLogger {
 
+    /** Prefix for the title added at the beginning of a {@link #dump(PrintWriter)} operation */
+    private static final String DUMP_TITLE_PREFIX = "Events log: ";
+
     /** Identifies the source of events. */
-    private final String mTag;
+    @Nullable private final String mTag;
 
     /** Stores the events using a ring buffer. */
     private final ArrayDeque<Event> mEvents;
@@ -55,7 +58,7 @@
      * @param size the maximum number of events to keep in log
      * @param tag the string displayed before the recorded log
      */
-    public EventLogger(int size, String tag) {
+    public EventLogger(int size, @Nullable String tag) {
         mEvents = new ArrayDeque<>(size);
         mMemSize = size;
         mTag = tag;
@@ -64,10 +67,10 @@
     /** Enqueues {@code event} to be logged. */
     public synchronized void enqueue(Event event) {
         if (mEvents.size() >= mMemSize) {
-            mEvents.removeLast();
+            mEvents.removeFirst();
         }
 
-        mEvents.addFirst(event);
+        mEvents.addLast(event);
     }
 
     /**
@@ -91,13 +94,19 @@
         dump(pw, "" /* prefix */);
     }
 
+    protected String getDumpTitle() {
+        if (mTag == null) {
+            return DUMP_TITLE_PREFIX;
+        }
+        return DUMP_TITLE_PREFIX + mTag;
+    }
+
     /** Dumps events using {@link PrintWriter} with a certain indent. */
     public synchronized void dump(PrintWriter pw, String indent) {
-        pw.println(indent + "Events log: " + mTag);
+        pw.println(getDumpTitle());
 
-        String childrenIndention = indent + "  ";
         for (Event evt : mEvents) {
-            pw.println(childrenIndention + evt.toString());
+            pw.println(indent + evt.toString());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 12424c0..e099aac 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8918,9 +8918,7 @@
         }
 
         if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
-                && getParent().getConfiguration().orientation == ORIENTATION_PORTRAIT
-                && getParent().getWindowConfiguration().getWindowingMode()
-                        == WINDOWING_MODE_FULLSCREEN) {
+                && isParentFullscreenPortrait()) {
             // We are using the parent configuration here as this is the most recent one that gets
             // passed to onConfigurationChanged when a relevant change takes place
             return info.getMinAspectRatio();
@@ -8943,6 +8941,13 @@
         return info.getMinAspectRatio();
     }
 
+    private boolean isParentFullscreenPortrait() {
+        final WindowContainer parent = getParent();
+        return parent != null
+                && parent.getConfiguration().orientation == ORIENTATION_PORTRAIT
+                && parent.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+    }
+
     /**
      * Returns true if the activity has maximum or minimum aspect ratio.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index d7c5e93..719f72c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -399,8 +399,11 @@
      * @return The intercepting intent if needed.
      */
     private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
+        if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
+            return null;
+        }
         if ((aInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0
-                || !mService.mAmInternal.shouldConfirmCredentials(userId)) {
+                && (mUserManager.isUserUnlocked(userId) || aInfo.directBootAware)) {
             return null;
         }
         final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0b16a4d..5c83c4f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -53,7 +53,9 @@
 import static android.content.pm.ActivityInfo.launchModeToString;
 import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -2139,6 +2141,11 @@
                 if (actuallyMoved) {
                     // Only record if the activity actually moved.
                     mMovedToTopActivity = act;
+                    if (mNoAnimation) {
+                        act.mDisplayContent.prepareAppTransition(TRANSIT_NONE);
+                    } else {
+                        act.mDisplayContent.prepareAppTransition(TRANSIT_TO_FRONT);
+                    }
                 }
                 act.updateOptionsLocked(mOptions);
                 deliverNewIntent(act, intentGrants);
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index d345227..cd26e2e 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -226,6 +226,9 @@
         }
 
         private void setReady(boolean ready) {
+            if (mReady == ready) {
+                return;
+            }
             ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
             mReady = ready;
             if (!ready) return;
@@ -239,7 +242,9 @@
             ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Adding to group: %s", mSyncId, wc);
             wc.setSyncGroup(this);
             wc.prepareSync();
-            mWm.mWindowPlacerLocked.requestTraversal();
+            if (mReady) {
+                mWm.mWindowPlacerLocked.requestTraversal();
+            }
         }
 
         void onCancelSync(WindowContainer wc) {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index bedeabe..89f1bd0 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -343,7 +343,11 @@
             if (childArea == null) {
                 continue;
             }
-            pw.println(prefix + "* " + childArea.getName());
+            pw.print(prefix + "* " + childArea.getName());
+            if (childArea.isOrganized()) {
+                pw.print(" (organized)");
+            }
+            pw.println();
             if (childArea.isTaskDisplayArea()) {
                 // TaskDisplayArea can only contain task. And it is already printed by display.
                 continue;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d1122e1..690779d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3516,9 +3516,8 @@
 
     @Override
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
-        super.dump(pw, prefix, dumpAll);
         pw.print(prefix);
-        pw.println("Display: mDisplayId=" + mDisplayId + " rootTasks=" + getRootTaskCount());
+        pw.println("Display: mDisplayId=" + mDisplayId + (isOrganized() ? " (organized)" : ""));
         final String subPrefix = "  " + prefix;
         pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
         pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -3549,6 +3548,7 @@
         pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
 
         pw.println();
+        super.dump(pw, prefix, dumpAll);
         pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
 
         pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
@@ -3640,6 +3640,7 @@
         pw.println();
         mInsetsStateController.dump(prefix, pw);
         mDwpcHelper.dump(prefix, pw);
+        pw.println();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d53ee1e..64cca87 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3407,7 +3407,6 @@
             final DisplayContent display = getChildAt(i);
             display.dump(pw, prefix, dumpAll);
         }
-        pw.println();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d3a3cf5..443e304 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1151,8 +1151,16 @@
         }
 
         next.delayedResume = false;
-        final TaskDisplayArea taskDisplayArea = getDisplayArea();
 
+        // If we are currently pausing an activity, then don't do anything until that is done.
+        final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+        if (!allPausedComplete) {
+            ProtoLog.v(WM_DEBUG_STATES,
+                    "resumeTopActivity: Skip resume: some activity pausing.");
+            return false;
+        }
+
+        final TaskDisplayArea taskDisplayArea = getDisplayArea();
         // If the top activity is the resumed one, nothing to do.
         if (mResumedActivity == next && next.isState(RESUMED)
                 && taskDisplayArea.allResumedActivitiesComplete()) {
@@ -1175,14 +1183,6 @@
             return false;
         }
 
-        // If we are currently pausing an activity, then don't do anything until that is done.
-        final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
-        if (!allPausedComplete) {
-            ProtoLog.v(WM_DEBUG_STATES,
-                    "resumeTopActivity: Skip resume: some activity pausing.");
-            return false;
-        }
-
         // If we are sleeping, and there is no resumed activity, and the top activity is paused,
         // well that is the state we want.
         if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
@@ -2580,6 +2580,14 @@
         return false;
     }
 
+    @Override
+    boolean canCustomizeAppTransition() {
+        // This is only called when the app transition is going to be played by system server. In
+        // this case, we should allow custom app transition for fullscreen embedded TaskFragment
+        // just like Activity.
+        return isEmbedded() && matchParentBounds();
+    }
+
     /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
     void clearLastPausedActivity() {
         forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 6d149da..da73fad 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -278,31 +278,29 @@
         // is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
         // different, we should recalcuating the bounds.
         boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = false;
-        // shouldSetAsOverrideWindowingMode is set if the task needs to retain the launchMode
-        // regardless of the windowing mode of the parent.
-        boolean shouldSetAsOverrideWindowingMode = false;
-        if (launchMode == WINDOWING_MODE_PINNED) {
-            if (DEBUG) appendLog("picture-in-picture");
-        } else if (!root.isResizeable()) {
-            if (shouldLaunchUnresizableAppInFreeformInFreeformMode(root, suggestedDisplayArea,
-                    options)) {
-                launchMode = WINDOWING_MODE_UNDEFINED;
-                if (outParams.mBounds.isEmpty()) {
-                    getTaskBounds(root, suggestedDisplayArea, layout, launchMode, hasInitialBounds,
-                            outParams.mBounds);
-                    hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
+        if (suggestedDisplayArea.inFreeformWindowingMode()) {
+            if (launchMode == WINDOWING_MODE_PINNED) {
+                if (DEBUG) appendLog("picture-in-picture");
+            } else if (!root.isResizeable()) {
+                if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, options)) {
+                    launchMode = WINDOWING_MODE_FREEFORM;
+                    if (outParams.mBounds.isEmpty()) {
+                        getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
+                                hasInitialBounds, outParams.mBounds);
+                        hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
+                    }
+                    if (DEBUG) appendLog("unresizable-freeform");
+                } else {
+                    launchMode = WINDOWING_MODE_FULLSCREEN;
+                    outParams.mBounds.setEmpty();
+                    if (DEBUG) appendLog("unresizable-forced-maximize");
                 }
-                if (DEBUG) appendLog("unresizable-freeform");
-            } else {
-                launchMode = WINDOWING_MODE_FULLSCREEN;
-                outParams.mBounds.setEmpty();
-                shouldSetAsOverrideWindowingMode = true;
-                if (DEBUG) appendLog("unresizable-forced-maximize");
             }
+        } else {
+            if (DEBUG) appendLog("non-freeform-task-display-area");
         }
         // If launch mode matches display windowing mode, let it inherit from display.
         outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode()
-                && !shouldSetAsOverrideWindowingMode
                 ? WINDOWING_MODE_UNDEFINED : launchMode;
 
         if (phase == PHASE_WINDOWING_MODE) {
@@ -669,7 +667,7 @@
         inOutBounds.offset(xOffset, yOffset);
     }
 
-    private boolean shouldLaunchUnresizableAppInFreeformInFreeformMode(ActivityRecord activity,
+    private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
             TaskDisplayArea displayArea, @Nullable ActivityOptions options) {
         if (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
             // Do not launch the activity in freeform if it explicitly requested fullscreen mode.
@@ -682,7 +680,8 @@
         final int displayOrientation = orientationFromBounds(displayArea.getBounds());
         final int activityOrientation = resolveOrientation(activity, displayArea,
                 displayArea.getBounds());
-        if (displayOrientation != activityOrientation) {
+        if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM
+                && displayOrientation != activityOrientation) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 9cb13e4..2b11d54 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -160,9 +160,9 @@
     private SurfaceControl.Transaction mFinishTransaction = null;
 
     /**
-     * Contains change infos for both participants and all ancestors. We have to track ancestors
-     * because they are all promotion candidates and thus we need their start-states
-     * to be captured.
+     * Contains change infos for both participants and all remote-animatable ancestors. The
+     * ancestors can be the promotion candidates so their start-states need to be captured.
+     * @see #getAnimatableParent
      */
     final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>();
 
@@ -221,7 +221,8 @@
     }
 
     @Nullable
-    static Transition fromBinder(@NonNull IBinder token) {
+    static Transition fromBinder(@Nullable IBinder token) {
+        if (token == null) return null;
         try {
             return ((Token) token).mTransition.get();
         } catch (ClassCastException e) {
@@ -410,8 +411,9 @@
                 mSyncId, wc);
         // "snapshot" all parents (as potential promotion targets). Do this before checking
         // if this is already a participant in case it has since been re-parented.
-        for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
-                curr = curr.getParent()) {
+        for (WindowContainer<?> curr = getAnimatableParent(wc);
+                curr != null && !mChanges.containsKey(curr);
+                curr = getAnimatableParent(curr)) {
             mChanges.put(curr, new ChangeInfo(curr));
             if (isReadyGroup(curr)) {
                 mReadyTracker.addGroup(curr);
@@ -1271,6 +1273,16 @@
         return sb.toString();
     }
 
+    /** Returns the parent that the remote animator can animate or control. */
+    private static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
+        WindowContainer<?> parent = wc.getParent();
+        while (parent != null
+                && (!parent.canCreateRemoteAnimationTarget() && !parent.isOrganized())) {
+            parent = parent.getParent();
+        }
+        return parent;
+    }
+
     private static boolean reportIfNotTop(WindowContainer wc) {
         // Organized tasks need to be reported anyways because Core won't show() their surfaces
         // and we can't rely on onTaskAppeared because it isn't in sync.
@@ -1494,7 +1506,8 @@
             intermediates.clear();
             boolean foundParentInTargets = false;
             // Collect the intermediate parents between target and top changed parent.
-            for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
+            for (WindowContainer<?> p = getAnimatableParent(wc); p != null;
+                    p = getAnimatableParent(p)) {
                 final ChangeInfo parentChange = changes.get(p);
                 if (parentChange == null || !parentChange.hasChanged(p)) break;
                 if (p.mRemoteToken == null) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6522d93..3b30dd1 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -114,12 +114,6 @@
 
     private boolean mShouldUpdateZoom;
 
-    /**
-     * Temporary storage for taking a screenshot of the wallpaper.
-     * @see #screenshotWallpaperLocked()
-     */
-    private WindowState mTmpTopWallpaper;
-
     @Nullable private Point mLargestDisplaySize = null;
 
     private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
@@ -965,21 +959,16 @@
     }
 
     WindowState getTopVisibleWallpaper() {
-        mTmpTopWallpaper = null;
-
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
-            token.forAllWindows(w -> {
-                final WindowStateAnimator winAnim = w.mWinAnimator;
-                if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
-                    mTmpTopWallpaper = w;
-                    return true;
+            for (int i = token.getChildCount() - 1; i >= 0; i--) {
+                final WindowState w = token.getChildAt(i);
+                if (w.mWinAnimator.getShown() && w.mWinAnimator.mLastAlpha > 0f) {
+                    return w;
                 }
-                return false;
-            }, true /* traverseTopToBottom */);
+            }
         }
-
-        return mTmpTopWallpaper;
+        return null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 19409b1..73759d3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4927,7 +4927,8 @@
         // animation on the keyguard but seeing the IME window that originally on the app
         // which behinds the keyguard.
         final WindowState imeInputTarget = getImeInputTarget();
-        if (imeInputTarget != null && !(imeInputTarget.isDrawn() || imeInputTarget.isVisible())) {
+        if (imeInputTarget != null
+                && !(imeInputTarget.isDrawn() || imeInputTarget.isVisibleRequested())) {
             return false;
         }
         return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5d0551b..969056e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -310,7 +310,7 @@
             const InputDeviceIdentifier& identifier) override;
     std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override;
     TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
-                                                           int32_t surfaceRotation) override;
+                                                           ui::Rotation surfaceRotation) override;
 
     TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
     void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
@@ -1153,7 +1153,7 @@
 }
 
 TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
-        const std::string& inputDeviceDescriptor, int32_t surfaceRotation) {
+        const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
     JNIEnv* env = jniEnv();
 
     ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.c_str()));
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
index 59f27ec..c8e2676 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
@@ -334,7 +334,7 @@
     }
 
     @Test
-    public void testParseActivityTargetDisplayCategoryValid() throws Exception {
+    public void testParseActivityRequiredDisplayCategoryValid() throws Exception {
         final File testFile = extractFile(TEST_APP4_APK);
         String actualDisplayCategory = null;
         try {
@@ -342,7 +342,7 @@
             final List<ParsedActivity> activities = pkg.getActivities();
             for (ParsedActivity activity : activities) {
                 if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
-                    actualDisplayCategory = activity.getTargetDisplayCategory();
+                    actualDisplayCategory = activity.getRequiredDisplayCategory();
                 }
             }
         } finally {
@@ -352,7 +352,7 @@
     }
 
     @Test
-    public void testParseActivityTargetDisplayCategoryInvalid() throws Exception {
+    public void testParseActivityRequiredDisplayCategoryInvalid() throws Exception {
         final File testFile = extractFile(TEST_APP6_APK);
         String actualDisplayCategory = null;
         try {
@@ -360,12 +360,12 @@
             final List<ParsedActivity> activities = pkg.getActivities();
             for (ParsedActivity activity : activities) {
                 if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
-                    actualDisplayCategory = activity.getTargetDisplayCategory();
+                    actualDisplayCategory = activity.getRequiredDisplayCategory();
                 }
             }
         } catch (PackageManagerException e) {
             assertThat(e.getMessage()).contains(
-                    "targetDisplayCategory attribute can only consists"
+                    "requiredDisplayCategory attribute can only consist"
                             + " of alphanumeric characters, '_', and '.'");
         } finally {
             testFile.delete();
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 4ceae96..0e2e35f 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
@@ -54,7 +54,7 @@
         ParsedActivity::getTheme,
         ParsedActivity::getUiOptions,
         ParsedActivity::isSupportsSizeChanges,
-        ParsedActivity::getTargetDisplayCategory
+        ParsedActivity::getRequiredDisplayCategory
     )
 
     override fun mainComponentSubclassExtraParams() = listOf(
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 8d2e1ec..a8d8945 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -18,6 +18,10 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.server.app.GameManagerService.CANCEL_GAME_LOADING_MODE;
+import static com.android.server.app.GameManagerService.LOADING_BOOST_MAX_DURATION;
+import static com.android.server.app.GameManagerService.SET_GAME_STATE;
+import static com.android.server.app.GameManagerService.WRITE_DELAY_MILLIS;
+import static com.android.server.app.GameManagerService.WRITE_GAME_MODE_INTERVENTION_LIST_FILE;
 import static com.android.server.app.GameManagerService.WRITE_SETTINGS;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -59,6 +63,7 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.hardware.power.Mode;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PowerManagerInternal;
@@ -104,7 +109,8 @@
     private static final String PACKAGE_NAME_INVALID = "com.android.app";
     private static final int USER_ID_1 = 1001;
     private static final int USER_ID_2 = 1002;
-    private static final int DEFAULT_PACKAGE_UID = 12345;
+    // to pass the valid package check in some of the server methods
+    private static final int DEFAULT_PACKAGE_UID = Binder.getCallingUid();
 
     private MockitoSession mMockingSession;
     private String mPackageName;
@@ -205,28 +211,33 @@
                 .startMocking();
         mMockContext = new MockContext(InstrumentationRegistry.getContext());
         mPackageName = mMockContext.getPackageName();
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
-        applicationInfo.packageName = mPackageName;
-        final PackageInfo pi = new PackageInfo();
-        pi.packageName = mPackageName;
-        pi.applicationInfo = applicationInfo;
-        final List<PackageInfo> packages = new ArrayList<>();
-        packages.add(pi);
-
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME);
         final Resources resources =
                 InstrumentationRegistry.getInstrumentation().getContext().getResources();
         when(mMockPackageManager.getResourcesForApplication(anyString()))
                 .thenReturn(resources);
-        when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
-                .thenReturn(packages);
-        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
-                .thenReturn(applicationInfo);
         when(mMockPackageManager.getPackageUidAsUser(mPackageName, USER_ID_1)).thenReturn(
                 DEFAULT_PACKAGE_UID);
         LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
     }
 
+    private void mockAppCategory(String packageName, @ApplicationInfo.Category int category)
+            throws Exception {
+        reset(mMockPackageManager);
+        final ApplicationInfo gameApplicationInfo = new ApplicationInfo();
+        gameApplicationInfo.category = category;
+        gameApplicationInfo.packageName = packageName;
+        final PackageInfo pi = new PackageInfo();
+        pi.packageName = packageName;
+        pi.applicationInfo = gameApplicationInfo;
+        final List<PackageInfo> packages = new ArrayList<>();
+        packages.add(pi);
+        when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
+                .thenReturn(packages);
+        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(gameApplicationInfo);
+    }
+
     @After
     public void tearDown() throws Exception {
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
@@ -457,17 +468,22 @@
      * By default game mode is set to STANDARD
      */
     @Test
-    public void testGameModeDefaultValue() {
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-
-        startUser(gameManagerService, USER_ID_1);
+    public void testGetGameMode_defaultValue() {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
         mockModifyGameModeGranted();
-
         assertEquals(GameManager.GAME_MODE_STANDARD,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
     }
 
+    @Test
+    public void testGetGameMode_nonGame() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        mockModifyGameModeGranted();
+        assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+    }
+
     /**
      * Test the default behaviour for a nonexistent user.
      */
@@ -614,16 +630,21 @@
             int... requiredAvailableModes) {
         Arrays.sort(requiredAvailableModes);
         // check getAvailableGameModes
-        int[] reportedAvailableModes = gameManagerService.getAvailableGameModes(mPackageName);
+        int[] reportedAvailableModes = gameManagerService.getAvailableGameModes(mPackageName,
+                USER_ID_1);
         Arrays.sort(reportedAvailableModes);
         assertArrayEquals(requiredAvailableModes, reportedAvailableModes);
 
         // check GetModeInfo.getAvailableGameModes
         GameModeInfo info = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
-        assertNotNull(info);
-        reportedAvailableModes = info.getAvailableGameModes();
-        Arrays.sort(reportedAvailableModes);
-        assertArrayEquals(requiredAvailableModes, reportedAvailableModes);
+        if (requiredAvailableModes.length == 0) {
+            assertNull(info);
+        } else {
+            assertNotNull(info);
+            reportedAvailableModes = info.getAvailableGameModes();
+            Arrays.sort(reportedAvailableModes);
+            assertArrayEquals(requiredAvailableModes, reportedAvailableModes);
+        }
     }
 
     private void checkReportedOptedInGameModes(GameManagerService gameManagerService,
@@ -726,6 +747,14 @@
                 GameManager.GAME_MODE_STANDARD, GameManager.GAME_MODE_CUSTOM);
     }
 
+    @Test
+    public void testDeviceConfig_nonGame() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1));
+    }
+
     /**
      * Override device config for performance mode exists and is valid.
      */
@@ -1443,12 +1472,10 @@
     }
 
     @Test
-    public void testGameStateLoadingRequiresPerformanceMode() {
+    public void testSetGameState_loadingRequiresPerformanceMode() {
         mockDeviceConfigNone();
         mockModifyGameModeGranted();
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-        startUser(gameManagerService, USER_ID_1);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
         GameState gameState = new GameState(true, GameState.MODE_NONE);
         gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
         mTestLooper.dispatchAll();
@@ -1509,9 +1536,7 @@
     public void testSetGameStateNotLoading() {
         mockDeviceConfigNone();
         mockModifyGameModeGranted();
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-        startUser(gameManagerService, USER_ID_1);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
         gameManagerService.setGameMode(
                 mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
         int testMode = GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE;
@@ -1523,12 +1548,24 @@
         assertEquals(testLabel, gameState.getLabel());
         assertEquals(testQuality, gameState.getQuality());
         gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
+        assertTrue(gameManagerService.mHandler.hasEqualMessages(SET_GAME_STATE, gameState));
         mTestLooper.dispatchAll();
         verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, false);
         assertFalse(
                 gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
     }
 
+    @Test
+    public void testSetGameState_nonGame() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+        mockDeviceConfigNone();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        GameState gameState = new GameState(true, GameState.MODE_NONE);
+        gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
+        assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
+    }
+
     private List<String> readGameModeInterventionList() throws Exception {
         final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(),
                 "system/game_mode_intervention.list");
@@ -1800,9 +1837,7 @@
     public void testUpdateCustomGameModeConfiguration_permissionDenied() {
         mockModifyGameModeDenied();
         mockDeviceConfigAll();
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-        startUser(gameManagerService, USER_ID_1);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
         assertThrows(SecurityException.class, () -> {
             gameManagerService.updateCustomGameModeConfiguration(mPackageName,
                     new GameModeConfiguration.Builder().setScalingFactor(0.5f).build(),
@@ -1813,9 +1848,7 @@
     @Test
     public void testUpdateCustomGameModeConfiguration_noUserId() {
         mockModifyGameModeGranted();
-        GameManagerService gameManagerService =
-                new GameManagerService(mMockContext, mTestLooper.getLooper());
-        startUser(gameManagerService, USER_ID_2);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_2);
         assertThrows(IllegalArgumentException.class, () -> {
             gameManagerService.updateCustomGameModeConfiguration(mPackageName,
                     new GameModeConfiguration.Builder().setScalingFactor(0.5f).build(),
@@ -1824,6 +1857,63 @@
     }
 
     @Test
+    public void testUpdateCustomGameModeConfiguration_nonGame() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.updateCustomGameModeConfiguration(mPackageName,
+                new GameModeConfiguration.Builder().setScalingFactor(0.35f).setFpsOverride(
+                        60).build(),
+                USER_ID_1);
+        assertFalse(gameManagerService.mHandler.hasMessages(WRITE_SETTINGS));
+        GameManagerService.GamePackageConfiguration pkgConfig = gameManagerService.getConfig(
+                mPackageName, USER_ID_1);
+        assertNull(pkgConfig);
+    }
+
+    @Test
+    public void testUpdateCustomGameModeConfiguration() throws InterruptedException {
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.updateCustomGameModeConfiguration(mPackageName,
+                new GameModeConfiguration.Builder().setScalingFactor(0.35f).setFpsOverride(
+                        60).build(),
+                USER_ID_1);
+
+        assertTrue(gameManagerService.mHandler.hasEqualMessages(WRITE_SETTINGS, USER_ID_1));
+        assertTrue(
+                gameManagerService.mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
+                        USER_ID_1));
+
+        GameManagerService.GamePackageConfiguration pkgConfig = gameManagerService.getConfig(
+                mPackageName, USER_ID_1);
+        assertNotNull(pkgConfig);
+        GameManagerService.GamePackageConfiguration.GameModeConfiguration modeConfig =
+                pkgConfig.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+        assertNotNull(modeConfig);
+        assertEquals(modeConfig.getScaling(), 0.35f, 0.01f);
+        assertEquals(modeConfig.getFps(), 60);
+        // creates a new service to check that no data has been stored
+        mTestLooper.dispatchAll();
+        gameManagerService = createServiceAndStartUser(USER_ID_1);
+        pkgConfig = gameManagerService.getConfig(mPackageName, USER_ID_1);
+        assertNull(pkgConfig);
+
+        mTestLooper.moveTimeForward(WRITE_DELAY_MILLIS + 500);
+        mTestLooper.dispatchAll();
+        // creates a new service to check that data is persisted after delay
+        gameManagerService = createServiceAndStartUser(USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_STANDARD,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+        pkgConfig = gameManagerService.getConfig(mPackageName, USER_ID_1);
+        assertNotNull(pkgConfig);
+        modeConfig = pkgConfig.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+        assertNotNull(modeConfig);
+        assertEquals(modeConfig.getScaling(), 0.35f, 0.01f);
+        assertEquals(modeConfig.getFps(), 60);
+    }
+
+    @Test
     public void testWritingSettingFile_onShutdown() throws InterruptedException {
         mockModifyGameModeGranted();
         mockDeviceConfigAll();
@@ -1983,4 +2073,111 @@
         }
         folder.delete();
     }
+
+    @Test
+    public void testNotifyGraphicsEnvironmentSetup() {
+        String configString = "mode=2,loadingBoost=2000";
+        when(DeviceConfig.getProperty(anyString(), anyString()))
+                .thenReturn(configString);
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        gameManagerService.notifyGraphicsEnvironmentSetup(mPackageName, USER_ID_1);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, true);
+        reset(mMockPowerManager);
+        assertTrue(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+        mTestLooper.moveTimeForward(2000);
+        mTestLooper.dispatchAll();
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, false);
+    }
+
+    @Test
+    public void testNotifyGraphicsEnvironmentSetup_outOfBoundBoostValue() {
+        String configString = "mode=2,loadingBoost=0:mode=3,loadingBoost=7000";
+        when(DeviceConfig.getProperty(anyString(), anyString()))
+                .thenReturn(configString);
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        gameManagerService.notifyGraphicsEnvironmentSetup(mPackageName, USER_ID_1);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, true);
+        reset(mMockPowerManager);
+        assertTrue(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+        mTestLooper.moveTimeForward(100);
+        mTestLooper.dispatchAll();
+        // 0 loading boost value should still trigger max timeout
+        verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+        assertTrue(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+        mTestLooper.moveTimeForward(LOADING_BOOST_MAX_DURATION);
+        mTestLooper.dispatchAll();
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, false);
+        reset(mMockPowerManager);
+        assertFalse(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+
+        // 7000 loading boost value should exceed the max timeout of 5s and be bounded
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
+        gameManagerService.notifyGraphicsEnvironmentSetup(mPackageName, USER_ID_1);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, true);
+        reset(mMockPowerManager);
+        assertTrue(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+        mTestLooper.moveTimeForward(LOADING_BOOST_MAX_DURATION);
+        mTestLooper.dispatchAll();
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, false);
+        assertFalse(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+    }
+
+    @Test
+    public void testNotifyGraphicsEnvironmentSetup_noDeviceConfig() {
+        mockDeviceConfigNone();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.notifyGraphicsEnvironmentSetup(mPackageName, USER_ID_1);
+        verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+        assertFalse(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+    }
+
+    @Test
+    public void testNotifyGraphicsEnvironmentSetup_noLoadingBoostValue() {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.notifyGraphicsEnvironmentSetup(mPackageName, USER_ID_1);
+        verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+        assertFalse(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+    }
+
+    @Test
+    public void testNotifyGraphicsEnvironmentSetup_nonGame() throws Exception {
+        String configString = "mode=2,loadingBoost=2000";
+        when(DeviceConfig.getProperty(anyString(), anyString()))
+                .thenReturn(configString);
+        mockModifyGameModeGranted();
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+        gameManagerService.notifyGraphicsEnvironmentSetup(mPackageName, USER_ID_1);
+        verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+        assertFalse(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+    }
+
+    @Test
+    public void testNotifyGraphicsEnvironmentSetup_differentApp() throws Exception {
+        String configString = "mode=2,loadingBoost=2000";
+        when(DeviceConfig.getProperty(anyString(), anyString()))
+                .thenReturn(configString);
+        mockModifyGameModeGranted();
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String someGamePkg = "some.game";
+        mockAppCategory(someGamePkg, ApplicationInfo.CATEGORY_GAME);
+        when(mMockPackageManager.getPackageUidAsUser(someGamePkg, USER_ID_1)).thenReturn(
+                DEFAULT_PACKAGE_UID + 1);
+        gameManagerService.setGameMode(someGamePkg, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+                gameManagerService.getGameMode(someGamePkg, USER_ID_1));
+        gameManagerService.notifyGraphicsEnvironmentSetup(someGamePkg, USER_ID_1);
+        verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+        assertFalse(gameManagerService.mHandler.hasMessages(CANCEL_GAME_LOADING_MODE));
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
new file mode 100644
index 0000000..7ab1363
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.os.ServiceManager;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public final class CpuMonitorServiceTest extends ExtendedMockitoTestCase {
+    private static final CpuAvailabilityMonitoringConfig TEST_CPU_AVAILABILITY_MONITORING_CONFIG =
+            new CpuAvailabilityMonitoringConfig.Builder(CPUSET_ALL)
+                    .addThreshold(30).addThreshold(70).build();
+
+    private static final CpuAvailabilityMonitoringConfig TEST_CPU_AVAILABILITY_MONITORING_CONFIG_2 =
+            new CpuAvailabilityMonitoringConfig.Builder(CPUSET_ALL)
+                    .addThreshold(10).addThreshold(90).build();
+
+    @Mock
+    private Context mContext;
+    private CpuMonitorService mService;
+    private HandlerExecutor mHandlerExecutor;
+    private CpuMonitorInternal mLocalService;
+
+    @Override
+    protected void initializeSession(StaticMockitoSessionBuilder builder) {
+        builder.mockStatic(ServiceManager.class);
+    }
+
+    @Before
+    public void setUp() {
+        mService = new CpuMonitorService(mContext);
+        mHandlerExecutor = new HandlerExecutor(new Handler(Looper.getMainLooper()));
+        doNothing().when(() -> ServiceManager.addService(eq("cpu_monitor"), any(Binder.class),
+                anyBoolean(), anyInt()));
+        mService.onStart();
+        mLocalService = LocalServices.getService(CpuMonitorInternal.class);
+    }
+
+    @After
+    public void tearDown() {
+        // The CpuMonitorInternal.class service is added by the mService.onStart call.
+        // Remove the service to ensure the setUp procedure can add this service again.
+        LocalServices.removeServiceForTest(CpuMonitorInternal.class);
+    }
+
+    @Test
+    public void testAddRemoveCpuAvailabilityCallback() {
+        CpuMonitorInternal.CpuAvailabilityCallback mockCallback = mock(
+                CpuMonitorInternal.CpuAvailabilityCallback.class);
+
+        mLocalService.addCpuAvailabilityCallback(mHandlerExecutor,
+                TEST_CPU_AVAILABILITY_MONITORING_CONFIG, mockCallback);
+
+        // TODO(b/242722241): Verify that {@link mockCallback.onAvailabilityChanged} and
+        //  {@link mockCallback.onMonitoringIntervalChanged} are called when the callback is added.
+
+        mLocalService.removeCpuAvailabilityCallback(mockCallback);
+    }
+
+
+    @Test
+    public void testDuplicateAddCpuAvailabilityCallback() {
+        CpuMonitorInternal.CpuAvailabilityCallback mockCallback = mock(
+                CpuMonitorInternal.CpuAvailabilityCallback.class);
+
+        mLocalService.addCpuAvailabilityCallback(mHandlerExecutor,
+                TEST_CPU_AVAILABILITY_MONITORING_CONFIG, mockCallback);
+
+        mLocalService.addCpuAvailabilityCallback(mHandlerExecutor,
+                TEST_CPU_AVAILABILITY_MONITORING_CONFIG_2, mockCallback);
+
+        // TODO(b/242722241): Verify that {@link mockCallback} is called only when CPU availability
+        //  thresholds cross the bounds specified in the
+        //  {@link TEST_CPU_AVAILABILITY_MONITORING_CONFIG_2} config.
+
+        mLocalService.removeCpuAvailabilityCallback(mockCallback);
+    }
+
+    @Test
+    public void testRemoveInvalidCpuAvailabilityCallback() {
+        CpuMonitorInternal.CpuAvailabilityCallback mockCallback = mock(
+                CpuMonitorInternal.CpuAvailabilityCallback.class);
+
+        mLocalService.removeCpuAvailabilityCallback(mockCallback);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 4c28c51..f2cba40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -237,8 +237,8 @@
         when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
         when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
         when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
-        when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
-        when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+        when(mLogicalDisplayMock.isEnabledLocked()).thenReturn(true);
+        when(mLogicalDisplayMock.isInTransitionLocked()).thenReturn(false);
         when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
         when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
         when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 9a4bb22..4f8cb88 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -219,8 +219,8 @@
         when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
         when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
         when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
-        when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
-        when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+        when(mLogicalDisplayMock.isEnabledLocked()).thenReturn(true);
+        when(mLogicalDisplayMock.isInTransitionLocked()).thenReturn(false);
         when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
         when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
         when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
@@ -233,4 +233,4 @@
                 });
         when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/AsyncUserVisibilityListener.java b/services/tests/mockingservicestests/src/com/android/server/pm/AsyncUserVisibilityListener.java
index a97491d..ddfbf16 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/AsyncUserVisibilityListener.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/AsyncUserVisibilityListener.java
@@ -30,7 +30,6 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-
 /**
  * {@link UserVisibilityListener} implementation that expects callback events to be asynchronously
  * received.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
index c203831..4487d13 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -329,6 +329,15 @@
         listener.verify();
     }
 
+    @Test
+    public final void testOnSystemUserVisibilityChanged() throws Exception {
+        AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(USER_SYSTEM));
+
+        mMediator.onSystemUserVisibilityChanged(/* visible= */ true);
+
+        listener.verify();
+    }
+
     /**
      * Starts a user in foreground on the default display, asserting it was properly started.
      *
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 1c43097..80305fa 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -114,6 +114,7 @@
         ":SimpleServiceTestApp3",
         ":StubTestApp",
         ":SuspendTestApp",
+        ":MediaButtonReceiverHolderTestHelperApp",
     ],
 
     java_resources: [
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 0be321a..9eb3b92 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -110,6 +110,9 @@
 
     <queries>
         <package android:name="com.android.servicestests.apps.suspendtestapp" />
+        <intent>
+            <action android:name="android.media.browse.MediaBrowserService" />
+        </intent>
     </queries>
 
     <!-- Uses API introduced in O (26) -->
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 9052f58..d967647 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -33,6 +33,7 @@
         <option name="test-file-name" value="SimpleServiceTestApp1.apk" />
         <option name="test-file-name" value="SimpleServiceTestApp2.apk" />
         <option name="test-file-name" value="SimpleServiceTestApp3.apk" />
+        <option name="test-file-name" value="MediaButtonReceiverHolderTestHelperApp.apk" />
     </target_preparer>
 
     <!-- Create place to store apks -->
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index a49214f..537d04f 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -38,9 +38,6 @@
 import static com.android.server.am.UserController.USER_CURRENT_MSG;
 import static com.android.server.am.UserController.USER_START_MSG;
 import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static com.android.server.am.UserController.USER_VISIBILITY_CHANGED_MSG;
-import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
-import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
 
 import static com.google.android.collect.Lists.newArrayList;
 import static com.google.android.collect.Sets.newHashSet;
@@ -102,7 +99,6 @@
 import com.android.server.SystemService;
 import com.android.server.am.UserState.KeyEvictedCallback;
 import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerInternal.UserAssignmentResult;
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
@@ -117,6 +113,8 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Tests for {@link UserController}.
@@ -150,9 +148,11 @@
 
     private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
             Intent.ACTION_USER_STARTED,
-            Intent.ACTION_USER_SWITCHED,
             Intent.ACTION_USER_STARTING);
 
+    private static final List<String> START_FOREGROUND_USER_DEFERRED_ACTIONS = newArrayList(
+            Intent.ACTION_USER_SWITCHED);
+
     private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
             Intent.ACTION_USER_STARTED,
             Intent.ACTION_LOCKED_BOOT_COMPLETED,
@@ -162,18 +162,12 @@
             REPORT_USER_SWITCH_MSG,
             USER_SWITCH_TIMEOUT_MSG,
             USER_START_MSG,
-            USER_VISIBILITY_CHANGED_MSG,
             USER_CURRENT_MSG);
 
-    private static final Set<Integer> START_INVISIBLE_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
+    private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
             USER_START_MSG,
             REPORT_LOCKED_BOOT_COMPLETE_MSG);
 
-    private static final Set<Integer> START_VISIBLE_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
-            USER_START_MSG,
-            USER_VISIBILITY_CHANGED_MSG,
-            REPORT_LOCKED_BOOT_COMPLETE_MSG);
-
     @Before
     public void setUp() throws Exception {
         runWithDexmakerShareClassLoader(() -> {
@@ -225,14 +219,12 @@
 
     @Test
     public void testStartUser_background() {
-        mockAssignUserToMainDisplay(TEST_USER_ID, /* foreground= */ false,
-                USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
         boolean started = mUserController.startUser(TEST_USER_ID, /* foreground= */ false);
         assertWithMessage("startUser(%s, foreground=false)", TEST_USER_ID).that(started).isTrue();
         verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
         verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
         verify(mInjector, never()).clearAllLockedTasks(anyString());
-        startBackgroundUserAssertions(/*visible= */ false);
+        startBackgroundUserAssertions();
         verifyUserAssignedToDisplay(TEST_USER_ID, Display.DEFAULT_DISPLAY);
     }
 
@@ -267,7 +259,7 @@
         verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
         verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
         verify(mInjector, never()).clearAllLockedTasks(anyString());
-        startBackgroundUserAssertions(/*visible= */ true);
+        startBackgroundUserAssertions();
     }
 
     @Test
@@ -293,8 +285,6 @@
 
     @Test
     public void testStartPreCreatedUser_background() throws Exception {
-        mockAssignUserToMainDisplay(TEST_PRE_CREATED_USER_ID, /* foreground= */ false,
-                USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
         assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false));
         // Make sure no intents have been fired for pre-created users.
         assertTrue(mInjector.mSentIntents.isEmpty());
@@ -322,10 +312,8 @@
         assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
     }
 
-    private void startBackgroundUserAssertions(boolean visible) {
-        startUserAssertions(START_BACKGROUND_USER_ACTIONS,
-                visible ? START_VISIBLE_BACKGROUND_USER_MESSAGE_CODES
-                        : START_INVISIBLE_BACKGROUND_USER_MESSAGE_CODES);
+    private void startBackgroundUserAssertions() {
+        startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
     }
 
     private void startForegroundUserAssertions() {
@@ -413,11 +401,11 @@
     private void continueAndCompleteUserSwitch(UserState userState, int oldUserId, int newUserId) {
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
         mInjector.mHandler.removeMessages(UserController.COMPLETE_USER_SWITCH_MSG);
-        mUserController.completeUserSwitch(newUserId);
+        mUserController.completeUserSwitch(oldUserId, newUserId);
     }
 
     @Test
-    public void testContinueUserSwitch() throws RemoteException {
+    public void testContinueUserSwitch() {
         mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
         // Start user -- this will update state of mUserController
@@ -432,12 +420,12 @@
         continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
         verify(mInjector, times(0)).dismissKeyguard(any(), anyString());
         verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
-        continueUserSwitchAssertions(TEST_USER_ID, false);
-        verifySystemUserVisibilityChangedNotified(/* visible= */ false);
+        continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
+        verifySystemUserVisibilityChangesNeverNotified();
     }
 
     @Test
-    public void testContinueUserSwitchDismissKeyguard() throws RemoteException {
+    public void testContinueUserSwitchDismissKeyguard() {
         when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false);
         mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
@@ -453,12 +441,12 @@
         continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
         verify(mInjector, times(1)).dismissKeyguard(any(), anyString());
         verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
-        continueUserSwitchAssertions(TEST_USER_ID, false);
-        verifySystemUserVisibilityChangedNotified(/* visible= */ false);
+        continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
+        verifySystemUserVisibilityChangesNeverNotified();
     }
 
     @Test
-    public void testContinueUserSwitchUIDisabled() throws RemoteException {
+    public void testContinueUserSwitchUIDisabled() {
         mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
 
@@ -473,11 +461,11 @@
         // Verify that continueUserSwitch worked as expected
         continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
         verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
-        continueUserSwitchAssertions(TEST_USER_ID, false);
+        continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
     }
 
-    private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping)
-            throws RemoteException {
+    private void continueUserSwitchAssertions(int expectedOldUserId, int expectedNewUserId,
+            boolean backgroundUserStopping) {
         Set<Integer> expectedCodes = new LinkedHashSet<>();
         expectedCodes.add(COMPLETE_USER_SWITCH_MSG);
         expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
@@ -489,7 +477,8 @@
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
         Message msg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG);
         assertNotNull(msg);
-        assertEquals("Unexpected userId", expectedUserId, msg.arg1);
+        assertEquals("Unexpected oldUserId", expectedOldUserId, msg.arg1);
+        assertEquals("Unexpected newUserId", expectedNewUserId, msg.arg2);
     }
 
     @Test
@@ -502,16 +491,21 @@
         mUserController.startUser(TEST_USER_ID, true);
         Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
+        int oldUserId = reportMsg.arg1;
         int newUserId = reportMsg.arg2;
         mInjector.mHandler.clearAllRecordedMessages();
         // Mockito can't reset only interactions, so just verify that this hasn't been
         // called with 'false' until after dispatchUserSwitchComplete.
         verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
         // Call dispatchUserSwitchComplete
-        mUserController.dispatchUserSwitchComplete(newUserId);
+        mUserController.dispatchUserSwitchComplete(oldUserId, newUserId);
         verify(observer, times(1)).onUserSwitchComplete(anyInt());
         verify(observer).onUserSwitchComplete(TEST_USER_ID);
         verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+        startUserAssertions(Stream.concat(
+                        START_FOREGROUND_USER_ACTIONS.stream(),
+                        START_FOREGROUND_USER_DEFERRED_ACTIONS.stream()
+                ).collect(Collectors.toList()), Collections.emptySet());
     }
 
     @Test
@@ -561,7 +555,7 @@
         assertFalse(mUserController.canStartMoreUsers());
         assertEquals(Arrays.asList(new Integer[] {0, TEST_USER_ID1, TEST_USER_ID2}),
                 mUserController.getRunningUsersLU());
-        verifySystemUserVisibilityChangedNotified(/* visible= */ false);
+        verifySystemUserVisibilityChangesNeverNotified();
     }
 
     /**
@@ -709,24 +703,19 @@
 
     @Test
     public void testStartProfile() throws Exception {
-        mockAssignUserToMainDisplay(TEST_PRE_CREATED_USER_ID, /* foreground= */ false,
-                USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
         setUpAndStartProfileInBackground(TEST_USER_ID1);
 
-        startBackgroundUserAssertions(/*visible= */ true);
+        startBackgroundUserAssertions();
         verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY);
     }
 
     @Test
     public void testStartProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception {
-        mockAssignUserToMainDisplay(TEST_USER_ID1, /* foreground= */ false,
-                USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
-
         mockIsUsersOnSecondaryDisplaysEnabled(true);
 
         setUpAndStartProfileInBackground(TEST_USER_ID1);
 
-        startBackgroundUserAssertions(/*visible= */ true);
+        startBackgroundUserAssertions();
         verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY);
     }
 
@@ -923,8 +912,7 @@
     }
 
     private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
-            int expectedNumberOfCalls, boolean expectOldUserStopping)
-            throws RemoteException {
+            int expectedNumberOfCalls, boolean expectOldUserStopping) {
         // Start user -- this will update state of mUserController
         mUserController.startUser(newUserId, true);
         Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -939,7 +927,7 @@
         continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
         verify(mInjector.getWindowManager(), times(expectedNumberOfCalls))
                 .stopFreezingScreen();
-        continueUserSwitchAssertions(newUserId, expectOldUserStopping);
+        continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping);
     }
 
     private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
@@ -983,13 +971,6 @@
         when(mInjector.isUsersOnSecondaryDisplaysEnabled()).thenReturn(value);
     }
 
-    private void mockAssignUserToMainDisplay(@UserIdInt int userId, boolean foreground,
-            @UserAssignmentResult int result) {
-        when(mInjector.mUserManagerInternalMock.assignUserToDisplayOnStart(eq(userId),
-                /* profileGroupId= */ anyInt(), eq(foreground), eq(Display.DEFAULT_DISPLAY)))
-                        .thenReturn(result);
-    }
-
     private void verifyUserAssignedToDisplay(@UserIdInt int userId, int displayId) {
         verify(mInjector.getUserManagerInternal()).assignUserToDisplayOnStart(eq(userId), anyInt(),
                 anyBoolean(), eq(displayId));
@@ -1008,8 +989,8 @@
         verify(mInjector.getUserManagerInternal(), never()).unassignUserFromDisplayOnStop(userId);
     }
 
-    private void verifySystemUserVisibilityChangedNotified(boolean visible) {
-        verify(mInjector).onUserVisibilityChanged(UserHandle.USER_SYSTEM, visible);
+    private void verifySystemUserVisibilityChangesNeverNotified() {
+        verify(mInjector, never()).onSystemUserVisibilityChanged(anyBoolean());
     }
 
     // Should be public to allow mocking
@@ -1154,8 +1135,8 @@
         }
 
         @Override
-        void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
-            Log.i(TAG, "onUserVisibilityChanged(" + userId + ", " + visible + ")");
+        void onSystemUserVisibilityChanged(boolean visible) {
+            Log.i(TAG, "onSystemUserVisibilityChanged(" + visible + ")");
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
index 582c78b..fde3422 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
@@ -52,6 +52,8 @@
     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";
+    private static final String PACKAGE_NAME_4 = "com.android.app4";
+
 
     private void writeFile(File file, byte[] data) {
         file.mkdirs();
@@ -69,16 +71,23 @@
         writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
                         "system/game-manager-service.xml"),
                 ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<packages>\n"
-                        + "  <package name=\"com.android.app1\" gameMode=\"1\">\n"
-                        + "  </package>\n"
+                        + "<packages>"
+                        + "\n" // app1: no package config setting
+                        + "\n" // app2: performance mode is selected with override
                         + "  <package name=\"com.android.app2\" gameMode=\"2\">\n"
                         + "     <gameModeConfig gameMode=\"2\" scaling=\"0.99\" "
                         + "useAngle=\"true\" fps=\"90\" loadingBoost=\"123\"></gameModeConfig>\n"
                         + "     <gameModeConfig gameMode=\"3\"></gameModeConfig>\n"
-                        + "  </package>\n"
+                        + "  </package>"
+                        + "\n" // app3: only battery mode is selected
                         + "  <package name=\"com.android.app3\" gameMode=\"3\">\n"
-                        + "  </package>\n"
+                        + "  </package>"
+                        + "\n" // app4: no game mode selected but custom game mode config
+                        + "  <package name=\"com.android.app4\">\n"
+                        + "     <gameModeConfig gameMode=\"4\" scaling=\"0.4\" "
+                        + "fps=\"30\"></gameModeConfig>\n"
+                        + "  </package>"
+                        + "\n"
                         + "</packages>\n").getBytes());
     }
 
@@ -115,14 +124,15 @@
         assertTrue(settings.readPersistentDataLocked());
 
         // test game modes
-        assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_1));
-        assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
-        assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
+        assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_1));
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE, settings.getGameModeLocked(PACKAGE_NAME_2));
+        assertEquals(GameManager.GAME_MODE_BATTERY, settings.getGameModeLocked(PACKAGE_NAME_3));
+        assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_4));
 
         // test game mode configs
         assertNull(settings.getConfigOverride(PACKAGE_NAME_1));
         assertNull(settings.getConfigOverride(PACKAGE_NAME_3));
-        final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
+        GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
         assertNotNull(config);
 
         assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD));
@@ -141,6 +151,14 @@
                 GameModeConfiguration.DEFAULT_LOADING_BOOST_DURATION);
         assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
         assertFalse(batteryConfig.getUseAngle());
+
+        config = settings.getConfigOverride(PACKAGE_NAME_4);
+        assertNotNull(config);
+        GameModeConfiguration customConfig = config.getGameModeConfiguration(
+                GameManager.GAME_MODE_CUSTOM);
+        assertNotNull(customConfig);
+        assertEquals(customConfig.getScaling(), 0.4f, 0.1f);
+        assertEquals(customConfig.getFps(), 30);
     }
 
     @Test
@@ -176,16 +194,20 @@
         writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
                         "system/game-manager-service.xml"),
                 ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<packages>\n"
+                        + "<packages>"
+                        + "\n" // missing package name
                         + "  <package gameMode=\"1\">\n"
-                        + "  </package>\n"
+                        + "  </package>"
+                        + "\n" // app2 with unknown sub element
                         + "  <package name=\"com.android.app2\" gameMode=\"2\">\n"
                         + "     <unknown></unknown>"
                         + "     <gameModeConfig gameMode=\"3\" fps=\"90\"></gameModeConfig>\n"
                         + "     foo bar"
-                        + "  </package>\n"
+                        + "  </package>"
+                        + "\n" // unknown package element
                         + "  <unknownTag></unknownTag>\n"
-                        + "    foo bar\n"
+                        + "    foo bar"
+                        + "\n" // app3 after unknown element
                         + "  <package name=\"com.android.app3\" gameMode=\"3\">\n"
                         + "  </package>\n"
                         + "</packages>\n").getBytes());
@@ -214,6 +236,8 @@
         settings.setGameModeLocked(PACKAGE_NAME_1, GameManager.GAME_MODE_BATTERY);
         settings.setGameModeLocked(PACKAGE_NAME_2, GameManager.GAME_MODE_PERFORMANCE);
         settings.setGameModeLocked(PACKAGE_NAME_3, GameManager.GAME_MODE_STANDARD);
+
+        // set config for app2
         GamePackageConfiguration config = new GamePackageConfiguration(PACKAGE_NAME_2);
         GameModeConfiguration performanceConfig = config.getOrAddDefaultGameModeConfiguration(
                 GameManager.GAME_MODE_PERFORMANCE);
@@ -225,18 +249,29 @@
                 GameManager.GAME_MODE_BATTERY);
         batteryConfig.setScaling(0.77f);
         settings.setConfigOverride(PACKAGE_NAME_2, config);
+
+        // set config for app4
+        config = new GamePackageConfiguration(PACKAGE_NAME_4);
+        GameModeConfiguration customConfig = config.getOrAddDefaultGameModeConfiguration(
+                GameManager.GAME_MODE_CUSTOM);
+        customConfig.setScaling(0.4f);
+        customConfig.setFpsStr("30");
+        settings.setConfigOverride(PACKAGE_NAME_4, config);
+
         settings.writePersistentDataLocked();
 
         // clear the settings in memory
         settings.removeGame(PACKAGE_NAME_1);
         settings.removeGame(PACKAGE_NAME_2);
         settings.removeGame(PACKAGE_NAME_3);
+        settings.removeGame(PACKAGE_NAME_4);
 
         // read back in and verify
         assertTrue(settings.readPersistentDataLocked());
         assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_1));
         assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
         assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3));
+        assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_4));
 
         config = settings.getConfigOverride(PACKAGE_NAME_1);
         assertNull(config);
@@ -256,5 +291,14 @@
         assertEquals(performanceConfig.getLoadingBoostDuration(), 321);
         assertEquals(performanceConfig.getFpsStr(), "60");
         assertTrue(performanceConfig.getUseAngle());
+
+        config = settings.getConfigOverride(PACKAGE_NAME_4);
+        assertNotNull(config);
+        customConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+        assertNotNull(customConfig);
+        assertEquals(customConfig.getScaling(), 0.4f, 0.1f);
+        assertEquals(customConfig.getFps(), 30);
+        assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
+        assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index afaee04..3daf0f8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -212,14 +212,14 @@
 
     private ArrayList<ActivityInfo> getActivityInfoList(
             String packageName, String name, boolean displayOnRemoveDevices,
-            String targetDisplayCategory) {
+            String requiredDisplayCategory) {
         ActivityInfo activityInfo = new ActivityInfo();
         activityInfo.packageName = packageName;
         activityInfo.name = name;
         activityInfo.flags = displayOnRemoveDevices
                 ? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
         activityInfo.applicationInfo = mApplicationInfoMock;
-        activityInfo.targetDisplayCategory = targetDisplayCategory;
+        activityInfo.requiredDisplayCategory = requiredDisplayCategory;
         return new ArrayList<>(Arrays.asList(activityInfo));
     }
 
@@ -317,7 +317,7 @@
         VirtualDeviceParams params = new VirtualDeviceParams
                 .Builder()
                 .setBlockedActivities(getBlockedActivities())
-                .addDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
+                .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
                 .build();
         mDeviceImpl = new VirtualDeviceImpl(mContext,
                 mAssociationInfo, new Binder(), /* ownerUid */ 0, VIRTUAL_DEVICE_ID,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
index a226ebc..aefe4b6 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
@@ -46,7 +46,7 @@
         VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder()
                 .setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED)
                 .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456)))
-                .addDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
+                .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
                 .addVirtualSensorConfig(
                         new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
                                 .setVendor(SENSOR_VENDOR)
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index ce35626..109abd0 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -319,7 +319,7 @@
 
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
-        final int displayIds[] = bs.getDisplayIds();
+        final int[] displayIds = bs.getDisplayIds(/* includeDisabled= */ true);
         final int size = displayIds.length;
         assertTrue(size > 0);
 
@@ -1417,7 +1417,8 @@
             DisplayManagerService.BinderService displayManagerBinderService,
             FakeDisplayDevice displayDevice) {
 
-        final int[] displayIds = displayManagerBinderService.getDisplayIds();
+        final int[] displayIds = displayManagerBinderService.getDisplayIds(
+                /* includeDisabled= */ true);
         assertTrue(displayIds.length > 0);
         int displayId = Display.INVALID_DISPLAY;
         for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 71f3d15..d1ae9ff 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -200,12 +200,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testDisplayModeVoting(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testDisplayModeVoting() {
         // With no votes present, DisplayModeDirector should allow any refresh rate.
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         DesiredDisplayModeSpecs modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
@@ -242,12 +237,7 @@
                         .isEqualTo((float) (minFps + i));
                 assertThat(modeSpecs.primary.physical.max)
                         .isEqualTo((float) (maxFps - i));
-                if (frameRateIsRefreshRate) {
-                    assertThat(modeSpecs.primary.render.min)
-                            .isEqualTo((float) (minFps + i));
-                } else {
-                    assertThat(modeSpecs.primary.render.min).isZero();
-                }
+                assertThat(modeSpecs.primary.render.min).isZero();
                 assertThat(modeSpecs.primary.render.max)
                         .isEqualTo((float) (maxFps - i));
                 if (priority >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF) {
@@ -255,12 +245,7 @@
                             .isEqualTo((float) (minFps + i));
                     assertThat(modeSpecs.appRequest.physical.max)
                             .isEqualTo((float) (maxFps - i));
-                    if (frameRateIsRefreshRate) {
-                        assertThat(modeSpecs.appRequest.render.min).isEqualTo(
-                                (float) (minFps + i));
-                    } else {
-                        assertThat(modeSpecs.appRequest.render.min).isZero();
-                    }
+                    assertThat(modeSpecs.appRequest.render.min).isZero();
                     assertThat(modeSpecs.appRequest.render.max).isEqualTo(
                             (float) (maxFps - i));
                 } else {
@@ -292,12 +277,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testVotingWithFloatingPointErrors(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testVotingWithFloatingPointErrors() {
         DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -318,12 +298,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle(
-            boolean frameRateIsRefreshRate) {
+    public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() {
         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
                 < Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
@@ -332,7 +307,6 @@
         assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
                 > Vote.PRIORITY_LOW_POWER_MODE);
 
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
         Display.Mode[] modes = new Display.Mode[4];
         modes[0] = new Display.Mode(
                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -408,17 +382,12 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testLPMHasHigherPriorityThanUser(boolean frameRateIsRefreshRate) {
+    public void testLPMHasHigherPriorityThanUser() {
         assertTrue(Vote.PRIORITY_LOW_POWER_MODE
                 > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
         assertTrue(Vote.PRIORITY_LOW_POWER_MODE
                 > Vote.PRIORITY_APP_REQUEST_SIZE);
 
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
         Display.Mode[] modes = new Display.Mode[4];
         modes[0] = new Display.Mode(
                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -443,11 +412,7 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
 
@@ -462,11 +427,7 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
 
@@ -481,11 +442,7 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(2);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
 
@@ -500,21 +457,13 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.baseModeId).isEqualTo(4);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestRefreshRateRange(boolean frameRateIsRefreshRate) {
+    public void testAppRequestRefreshRateRange() {
         // Confirm that the app request range doesn't include flicker or min refresh rate settings,
         // but does include everything else.
         assertTrue(
@@ -525,7 +474,6 @@
         assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE
                 >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
 
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
         Display.Mode[] modes = new Display.Mode[3];
         modes[0] = new Display.Mode(
                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
@@ -582,12 +530,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testSpecsFromRefreshRateSettings(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testSpecsFromRefreshRateSettings() {
         // Confirm that, with varying settings for min, peak, and default refresh rate,
         // DesiredDisplayModeSpecs is calculated correctly.
         float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f};
@@ -607,27 +550,12 @@
 
         RefreshRateRanges frameRateAll = new RefreshRateRanges(rangeAll, rangeAll);
         RefreshRateRanges frameRate90toInf = new RefreshRateRanges(range90toInf, range90toInf);
-        RefreshRateRanges frameRate0to60;
-        RefreshRateRanges frameRate0to90;
-        RefreshRateRanges frameRate0to120;
-        RefreshRateRanges frameRate60to90;
-        RefreshRateRanges frameRate90to90;
-        RefreshRateRanges frameRate90to120;
-        if (frameRateIsRefreshRate) {
-            frameRate0to60 = new RefreshRateRanges(range0to60, range0to60);
-            frameRate0to90 = new RefreshRateRanges(range0to90, range0to90);
-            frameRate0to120 = new RefreshRateRanges(range0to120, range0to120);
-            frameRate60to90 = new RefreshRateRanges(range60to90, range60to90);
-            frameRate90to90 = new RefreshRateRanges(range90to90, range90to90);
-            frameRate90to120 = new RefreshRateRanges(range90to120, range90to120);
-        } else {
-            frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
-            frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
-            frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
-            frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
-            frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
-            frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
-        }
+        RefreshRateRanges frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
+        RefreshRateRanges frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
+        RefreshRateRanges frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
+        RefreshRateRanges frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
+        RefreshRateRanges frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
+        RefreshRateRanges frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
 
         verifySpecsWithRefreshRateSettings(director, 0, 0, 0, frameRateAll, frameRateAll);
         verifySpecsWithRefreshRateSettings(director, 0, 0, 90, frameRate0to90, frameRateAll);
@@ -657,12 +585,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testBrightnessObserverCallWithRefreshRateSettings(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testBrightnessObserverCallWithRefreshRateSettings() {
         // Confirm that, with varying settings for min, peak, and default refresh rate, we make the
         // correct call to the brightness observer.
         float[] refreshRates = {60.f, 90.f, 120.f};
@@ -677,12 +600,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testVotingWithAlwaysRespectAppRequest(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testVotingWithAlwaysRespectAppRequest() {
         Display.Mode[] modes = new Display.Mode[3];
         modes[0] = new Display.Mode(
                 /*modeId=*/50, /*width=*/1000, /*height=*/1000, 50);
@@ -711,11 +629,7 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
 
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
@@ -734,23 +648,14 @@
 
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.baseModeId).isEqualTo(60);
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testVotingWithSwitchingTypeNone(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testVotingWithSwitchingTypeNone() {
         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -765,20 +670,11 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
 
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
-                    60);
-        } else {
-            assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
@@ -800,12 +696,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testVotingWithSwitchingTypeRenderFrameRateOnly(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testVotingWithSwitchingTypeRenderFrameRateOnly() {
         DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -820,20 +711,11 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
 
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
-                    60);
-        } else {
-            assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
@@ -846,20 +728,11 @@
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
-        } else {
-            assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
-        }
+        assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30);
-            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
-        } else {
-            assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
-            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
-        }
+        assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+        assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
 
         assertThat(desiredSpecs.baseModeId).isEqualTo(30);
     }
@@ -887,12 +760,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testDefaultDisplayModeIsSelectedIfAvailable(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testDefaultDisplayModeIsSelectedIfAvailable() {
         final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f};
         final int defaultModeId = 3;
         DisplayModeDirector director = createDirectorFromRefreshRateArray(
@@ -1173,17 +1041,12 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestMinRefreshRate(boolean frameRateIsRefreshRate) {
+    public void testAppRequestMinRefreshRate() {
         // Confirm that the app min request range doesn't include flicker or min refresh rate
         // settings but does include everything else.
         assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
                 >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
 
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
         Display.Mode[] modes = new Display.Mode[3];
         modes[0] = new Display.Mode(
                 /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
@@ -1225,11 +1088,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestMaxRefreshRate(boolean frameRateIsRefreshRate) {
+    public void testAppRequestMaxRefreshRate() {
         // Confirm that the app max request range doesn't include flicker or min refresh rate
         // settings but does include everything else.
         assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
@@ -1243,7 +1102,6 @@
         modes[2] = new Display.Mode(
                 /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
 
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
         DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1254,19 +1112,11 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.render.min).isZero();
-        }
+        assertThat(desiredSpecs.primary.render.min).isZero();
         assertThat(desiredSpecs.primary.render.max).isAtMost(60);
         assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
         assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.render.min).isAtMost(60f);
-        } else {
-            assertThat(desiredSpecs.appRequest.render.min).isZero();
-        }
+        assertThat(desiredSpecs.appRequest.render.min).isZero();
         assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f);
 
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
@@ -1288,30 +1138,16 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75);
         assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(75);
-        } else {
-            assertThat(desiredSpecs.primary.render.min).isZero();
-        }
+        assertThat(desiredSpecs.primary.render.min).isZero();
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(75);
         assertThat(desiredSpecs.appRequest.physical.min).isZero();
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
-                    75);
-        } else {
-            assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
-        }
+        assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
         assertThat(desiredSpecs.appRequest.render.min).isZero();
         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(75);
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestObserver_modeId(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testAppRequestObserver_modeId() {
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
 
@@ -1373,12 +1209,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestObserver_minRefreshRate(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testAppRequestObserver_minRefreshRate() {
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0);
         Vote appRequestRefreshRate =
@@ -1391,15 +1222,9 @@
         Vote appRequestRefreshRateRange =
                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
         assertNotNull(appRequestRefreshRateRange);
-        if (frameRateIsRefreshRate) {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min)
-                    .isWithin(FLOAT_TOLERANCE).of(60);
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90);
-        } else {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isPositiveInfinity();
-        }
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+                .isPositiveInfinity();
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
                 .isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
@@ -1417,15 +1242,9 @@
         appRequestRefreshRateRange =
                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
         assertNotNull(appRequestRefreshRateRange);
-        if (frameRateIsRefreshRate) {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isWithin(
-                    FLOAT_TOLERANCE).of(90);
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90);
-        } else {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isPositiveInfinity();
-        }
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+                .isPositiveInfinity();
 
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
                 .isWithin(FLOAT_TOLERANCE).of(90);
@@ -1435,12 +1254,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestObserver_maxRefreshRate(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testAppRequestObserver_maxRefreshRate() {
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90);
         Vote appRequestRefreshRate =
@@ -1454,13 +1268,8 @@
                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
         assertNotNull(appRequestRefreshRateRange);
         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        if (frameRateIsRefreshRate) {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isWithin(FLOAT_TOLERANCE).of(90);
-        } else {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isPositiveInfinity();
-        }
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+                .isPositiveInfinity();
 
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1480,13 +1289,8 @@
                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
         assertNotNull(appRequestRefreshRateRange);
         assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-        if (frameRateIsRefreshRate) {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isPositiveInfinity();
-        }
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+                .isPositiveInfinity();
 
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1512,12 +1316,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestObserver_modeIdAndRefreshRateRange(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testAppRequestObserver_modeIdAndRefreshRateRange() {
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
         director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
 
@@ -1547,16 +1346,9 @@
         Vote appRequestRefreshRateRange =
                 director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
         assertNotNull(appRequestRefreshRateRange);
-        if (frameRateIsRefreshRate) {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min)
-                    .isWithin(FLOAT_TOLERANCE).of(90);
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isWithin(FLOAT_TOLERANCE).of(90);
-        } else {
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
-            assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
-                    .isPositiveInfinity();
-        }
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+        assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+                .isPositiveInfinity();
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
                 .isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1566,12 +1358,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testAppRequestsIsTheDefaultMode(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testAppRequestsIsTheDefaultMode() {
         Display.Mode[] modes = new Display.Mode[2];
         modes[0] = new Display.Mode(
                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -1600,12 +1387,7 @@
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testDisableRefreshRateSwitchingVote(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testDisableRefreshRateSwitchingVote() {
         DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1650,8 +1432,8 @@
             "true",
             "false"
     })
-    public void testBaseModeIdInPrimaryRange(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testBaseModeIdInPrimaryRange(boolean supportsFrameRateOverride) {
+        when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
         DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1662,12 +1444,12 @@
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-            assertThat(desiredSpecs.baseModeId).isEqualTo(50);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+        if (supportsFrameRateOverride) {
             assertThat(desiredSpecs.baseModeId).isEqualTo(70);
+        } else {
+            assertThat(desiredSpecs.baseModeId).isEqualTo(50);
+
         }
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -1679,11 +1461,7 @@
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         assertThat(desiredSpecs.baseModeId).isEqualTo(55);
@@ -1697,12 +1475,11 @@
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
-            assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+        if (supportsFrameRateOverride) {
             assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(52);
+        } else {
+            assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
         }
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
         assertThat(desiredSpecs.baseModeId).isEqualTo(55);
@@ -1716,23 +1493,14 @@
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(58);
-        } else {
-            assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
-        }
+        assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
         assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
         assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(58);
         assertThat(desiredSpecs.baseModeId).isEqualTo(55);
     }
 
     @Test
-    @Parameters({
-            "true",
-            "false"
-    })
-    public void testStaleAppVote(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testStaleAppVote() {
         Display.Mode[] modes = new Display.Mode[4];
         modes[0] = new Display.Mode(
                 /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -1782,8 +1550,8 @@
             "true",
             "false"
     })
-    public void testRefreshRateIsSubsetOfFrameRate(boolean frameRateIsRefreshRate) {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+    public void testRefreshRateIsSubsetOfFrameRate(boolean supportsFrameRateOverride) {
+        when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1795,11 +1563,7 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
-        } else {
-            assertThat(desiredSpecs.appRequest.render.min).isZero();
-        }
+        assertThat(desiredSpecs.appRequest.render.min).isZero();
         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
 
         votes.clear();
@@ -1810,13 +1574,11 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
-            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(
-                    120);
-        } else {
-            assertThat(desiredSpecs.appRequest.render.min).isZero();
+        assertThat(desiredSpecs.appRequest.render.min).isZero();
+        if (supportsFrameRateOverride) {
             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+        } else {
+            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
         }
 
         votes.clear();
@@ -1827,13 +1589,12 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
-            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(
-                    120);
-        } else {
+        if (supportsFrameRateOverride) {
             assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(60);
             assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+        } else {
+            assertThat(desiredSpecs.appRequest.render.min).isZero();
+            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
         }
 
         votes.clear();
@@ -1844,17 +1605,12 @@
         desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
         assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
         assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
-        if (frameRateIsRefreshRate) {
-            assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
-        } else {
-            assertThat(desiredSpecs.appRequest.render.min).isZero();
-        }
+        assertThat(desiredSpecs.appRequest.render.min).isZero();
         assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
     }
 
     @Test
     public void testRenderFrameRateIsAchievableByPhysicalRefreshRate() {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false);
         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1872,8 +1628,34 @@
     }
 
     @Test
+    @Parameters({
+            "true",
+            "false"
+    })
+    public void testRenderFrameRateIncludesPhysicalRefreshRate(boolean supportsFrameRateOverride) {
+        when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
+        DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
+        SparseArray<Vote> votes = new SparseArray<>();
+        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+        votesByDisplay.put(DISPLAY_ID, votes);
+
+        votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+        votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+                Vote.forRenderFrameRates(0, 30));
+        director.injectVotesByDisplay(votesByDisplay);
+        DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+        assertThat(desiredSpecs.appRequest.physical.min).isZero();
+        assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
+        assertThat(desiredSpecs.appRequest.render.min).isZero();
+        if (supportsFrameRateOverride) {
+            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+        } else {
+            assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+        }
+    }
+
+    @Test
     public void testRenderFrameRateIsDroppedIfLowerPriorityThenBaseModeRefreshRate() {
-        when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false);
         DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -2692,7 +2474,7 @@
         }
 
         @Override
-        public boolean renderFrameRateIsPhysicalRefreshRate() {
+        public boolean supportsFrameRateOverride() {
             return true;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 657bda6..246945c 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -30,6 +30,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -52,6 +54,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.layout.Layout;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -59,6 +63,7 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -83,6 +88,7 @@
     @Mock Resources mResourcesMock;
     @Mock IPowerManager mIPowerManagerMock;
     @Mock IThermalService mIThermalServiceMock;
+    @Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy = new DeviceStateToLayoutMap();
 
     @Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
 
@@ -132,7 +138,8 @@
         mLooper = new TestLooper();
         mHandler = new Handler(mLooper.getLooper());
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo,
-                mListenerMock, new DisplayManagerService.SyncRoot(), mHandler);
+                mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
+                mDeviceStateToLayoutMapSpy);
     }
 
 
@@ -259,7 +266,8 @@
         add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0));
         add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
 
-        int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
+        int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID,
+                /* includeDisabled= */ true);
         assertEquals(3, ids.length);
         Arrays.sort(ids);
         assertEquals(DEFAULT_DISPLAY, ids[0]);
@@ -503,10 +511,183 @@
                 /* isBootCompleted= */true));
     }
 
+    @Test
+    public void testDeviceStateLocked() {
+        DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+                DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+        DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+                DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+
+        Layout layout = new Layout();
+        layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, true, true);
+        layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, false, false);
+        when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
+
+        layout = new Layout();
+        layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, false, false);
+        layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, true, true);
+        when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(layout);
+        when(mDeviceStateToLayoutMapSpy.get(2)).thenReturn(layout);
+
+        LogicalDisplay display1 = add(device1);
+        assertEquals(info(display1).address, info(device1).address);
+        assertEquals(DEFAULT_DISPLAY, id(display1));
+
+        LogicalDisplay display2 = add(device2);
+        assertEquals(info(display2).address, info(device2).address);
+        // We can only have one default display
+        assertEquals(DEFAULT_DISPLAY, id(display1));
+
+        mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+        advanceTime(1000);
+        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
+
+        mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+        advanceTime(1000);
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
+
+        mLogicalDisplayMapper.setDeviceStateLocked(2, false);
+        advanceTime(1000);
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
+        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
+    }
+
+    @Test
+    public void testEnabledAndDisabledDisplays() {
+        DisplayAddress displayAddressOne = new TestUtils.TestDisplayAddress();
+        DisplayAddress displayAddressTwo = new TestUtils.TestDisplayAddress();
+        DisplayAddress displayAddressThree = new TestUtils.TestDisplayAddress();
+
+        TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, "one",
+                Display.TYPE_INTERNAL, 600, 800,
+                DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+        TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, "two",
+                Display.TYPE_INTERNAL, 200, 800,
+                DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+        TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, "three",
+                Display.TYPE_INTERNAL, 600, 900,
+                DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+
+        Layout threeDevicesEnabledLayout = new Layout();
+        threeDevicesEnabledLayout.createDisplayLocked(
+                displayAddressOne,
+                /* isDefault= */ true,
+                /* isEnabled= */ true);
+        threeDevicesEnabledLayout.createDisplayLocked(
+                displayAddressTwo,
+                /* isDefault= */ false,
+                /* isEnabled= */ true);
+        threeDevicesEnabledLayout.createDisplayLocked(
+                displayAddressThree,
+                /* isDefault= */ false,
+                /* isEnabled= */ true);
+
+        when(mDeviceStateToLayoutMapSpy.get(DeviceStateToLayoutMap.STATE_DEFAULT))
+                .thenReturn(threeDevicesEnabledLayout);
+
+        LogicalDisplay display1 = add(device1);
+        LogicalDisplay display2 = add(device2);
+        LogicalDisplay display3 = add(device3);
+
+        // ensure 3 displays are returned
+        int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID, false);
+        assertEquals(3, ids.length);
+        Arrays.sort(ids);
+        assertEquals(DEFAULT_DISPLAY, ids[0]);
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device1,
+                /* includeDisabled= */ false));
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device2,
+                /* includeDisabled= */ false));
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device3,
+                /* includeDisabled= */ false));
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+                threeDevicesEnabledLayout.getByAddress(displayAddressOne).getLogicalDisplayId(),
+                /* includeDisabled= */ false));
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+                threeDevicesEnabledLayout.getByAddress(displayAddressTwo).getLogicalDisplayId(),
+                /* includeDisabled= */ false));
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+                threeDevicesEnabledLayout.getByAddress(displayAddressThree).getLogicalDisplayId(),
+                /* includeDisabled= */ false));
+
+        Layout oneDeviceEnabledLayout = new Layout();
+        oneDeviceEnabledLayout.createDisplayLocked(
+                displayAddressOne,
+                /* isDefault= */ true,
+                /* isEnabled= */ true);
+        oneDeviceEnabledLayout.createDisplayLocked(
+                displayAddressTwo,
+                /* isDefault= */ false,
+                /* isEnabled= */ false);
+        oneDeviceEnabledLayout.createDisplayLocked(
+                displayAddressThree,
+                /* isDefault= */ false,
+                /* isEnabled= */ false);
+
+        when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout);
+        when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout);
+
+        // 1) Set the new state
+        // 2) Mark the displays as STATE_OFF so that it can continue with transition
+        // 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state
+        // 4) Dispatch handler events.
+        mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+        advanceTime(1000);
+        final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
+                Process.SYSTEM_UID, false);
+        if (allDisplayIds.length != 1) {
+            throw new RuntimeException("Displays: \n"
+                    + mLogicalDisplayMapper.getDisplayLocked(device1).toString()
+                    + "\n" + mLogicalDisplayMapper.getDisplayLocked(device2).toString()
+                    + "\n" + mLogicalDisplayMapper.getDisplayLocked(device3).toString());
+        }
+        // ensure only one display is returned
+        assertEquals(1, allDisplayIds.length);
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device1,
+                /* includeDisabled= */ false));
+        assertNull(mLogicalDisplayMapper.getDisplayLocked(device2,
+                /* includeDisabled= */ false));
+        assertNull(mLogicalDisplayMapper.getDisplayLocked(device3,
+                /* includeDisabled= */ false));
+        assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+                oneDeviceEnabledLayout.getByAddress(displayAddressOne).getLogicalDisplayId(),
+                /* includeDisabled= */ false));
+        assertNull(mLogicalDisplayMapper.getDisplayLocked(
+                oneDeviceEnabledLayout.getByAddress(displayAddressTwo).getLogicalDisplayId(),
+                /* includeDisabled= */ false));
+        assertNull(mLogicalDisplayMapper.getDisplayLocked(
+                oneDeviceEnabledLayout.getByAddress(displayAddressThree).getLogicalDisplayId(),
+                /* includeDisabled= */ false));
+
+        // Now do it again to go back to state 1
+        mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+        advanceTime(1000);
+        final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
+                Process.SYSTEM_UID, false);
+
+        // ensure all three displays are returned
+        assertEquals(3, threeDisplaysEnabled.length);
+    }
+
     /////////////////
     // Helper Methods
     /////////////////
 
+    private void advanceTime(long timeMs) {
+        mLooper.moveTimeForward(1000);
+        mLooper.dispatchAll();
+    }
+
     private TestDisplayDevice createDisplayDevice(int type, int width, int height, int flags) {
         return createDisplayDevice(
                 new TestUtils.TestDisplayAddress(), /*  uniqueId */ "", type, width, height, flags);
@@ -575,6 +756,7 @@
     class TestDisplayDevice extends DisplayDevice {
         private DisplayDeviceInfo mInfo;
         private DisplayDeviceInfo mSentInfo;
+        private int mState;
 
         TestDisplayDevice() {
             super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index 5a43530..1d70fc6 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -126,12 +126,12 @@
         verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
         reset(t);
 
-        mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_DISABLED);
+        mLogicalDisplay.setEnabledLocked(false);
         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
         verify(t).setDisplayFlags(any(), eq(0));
         reset(t);
 
-        mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+        mLogicalDisplay.setEnabledLocked(true);
         mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
         verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
diff --git a/services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java b/services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java
new file mode 100644
index 0000000..1c4ee69
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaButtonReceiverHolderTest {
+
+    @Test
+    public void createMediaButtonReceiverHolder_resolvesNullComponentName() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        PendingIntent pi = PendingIntent.getBroadcast(context, /* requestCode= */ 0, intent,
+                PendingIntent.FLAG_IMMUTABLE);
+        MediaButtonReceiverHolder a = MediaButtonReceiverHolder.create(/* userId= */ 0, pi,
+                context.getPackageName());
+        Truth.assertWithMessage("Component name must match PendingIntent creator package.").that(
+                a.getComponentName()).isNull();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/OWNERS b/services/tests/servicestests/src/com/android/server/media/OWNERS
new file mode 100644
index 0000000..55ffde2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137631
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 136e79e..261156611 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -456,9 +456,8 @@
                         new DeviceState(1, "CLOSED", 0 /* flags */),
                         new DeviceState(2, "HALF_OPENED", 0 /* flags */)
                 }, mDeviceStateArrayCaptor.getValue());
-        // onStateChanged() should be called because the provider could not find the sensor.
-        verify(listener).onStateChanged(mIntegerCaptor.capture());
-        assertEquals(1, mIntegerCaptor.getValue().intValue());
+        // onStateChanged() should not be called because the provider could not find the sensor.
+        verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
     }
 
     private static Sensor newSensor(String name, String type) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
index aafc16d..febbffe 100644
--- a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
@@ -71,9 +71,10 @@
         }
 
         @Test
-        public void testThatPrintWriterProducesEmptyListFromEmptyLog() {
+        public void testThatPrintWriterProducesOnlyTitleFromEmptyLog() {
             mEventLogger.dump(mTestPrintWriter);
-            assertThat(mTestStringWriter.toString()).isEmpty();
+            assertThat(mTestStringWriter.toString())
+                    .isEqualTo(mEventLogger.getDumpTitle() + "\n");
         }
     }
 
@@ -87,27 +88,27 @@
                         // insertion order, max size is 3
                         new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2 },
                         // expected events
-                        new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_1 }
+                        new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2 }
                     },
                     {
                         // insertion order, max size is 3
                         new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_3, TEST_EVENT_2 },
                         // expected events
-                        new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_3, TEST_EVENT_1 }
+                        new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_3, TEST_EVENT_2 }
                     },
                     {
                         // insertion order, max size is 3
                         new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2, TEST_EVENT_3,
                             TEST_EVENT_4 },
                         // expected events
-                        new EventLogger.Event[] { TEST_EVENT_4, TEST_EVENT_3, TEST_EVENT_2 }
+                        new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_3, TEST_EVENT_4 }
                     },
                     {
                         // insertion order, max size is 3
                         new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2, TEST_EVENT_3,
                             TEST_EVENT_4, TEST_EVENT_5 },
                         // expected events
-                        new EventLogger.Event[] { TEST_EVENT_5, TEST_EVENT_4, TEST_EVENT_3 }
+                        new EventLogger.Event[] { TEST_EVENT_3, TEST_EVENT_4, TEST_EVENT_5 }
                     }
             });
         }
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp
new file mode 100644
index 0000000..f376b6f
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "MediaButtonReceiverHolderTestHelperApp",
+
+    sdk_version: "current",
+
+    srcs: ["**/*.java"],
+
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml
new file mode 100644
index 0000000..3ba3dc2
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.mediabuttonreceiverholdertesthelperapp">
+
+    <application>
+        <receiver
+            android:name=".FakeMediaButtonBroadcastReceiver"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_BUTTON" />
+            </intent-filter>
+        </receiver>
+    </application>
+
+</manifest>
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS
new file mode 100644
index 0000000..55ffde2
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137631
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java
new file mode 100644
index 0000000..6fdd8be
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.servicestests.apps.mediabuttonreceiverholdertesthelperapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class FakeMediaButtonBroadcastReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "FakeMediaButtonBroadcastReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.v(TAG, "onReceive not expected");
+    }
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
index 4dcb442..5451735 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -32,7 +32,7 @@
 
 	    <activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
 	              android:exported="true"
-	              android:targetDisplayCategory="automotive">
+	              android:requiredDisplayCategory="automotive">
 	        <property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" />
 	        <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
 	        <property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" />
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
index 8e694e1..601479d 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
@@ -21,7 +21,7 @@
     <application>
         <activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
                   android:exported="true"
-                  android:targetDisplayCategory="$automotive">
+                  android:requiredDisplayCategory="$automotive">
         </activity>
     </application>
 </manifest>
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 1575336..8a15c30 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -28,6 +28,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
@@ -275,11 +276,60 @@
         // THEN calling intercept returns true
         mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
 
-        // THEN the returned intent is the quiet mode intent
+        // THEN the returned intent is the confirm credentials intent
         assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
     }
 
     @Test
+    public void testLockedManagedProfileShowWhenLocked() {
+        Intent originalIntent = new Intent();
+        // GIVEN that the user is locked but its storage is unlocked and the activity has
+        // showWhenLocked flag
+        when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+        when(mUserManager.isUserUnlocked(eq(TEST_USER_ID))).thenReturn(true);
+        mAInfo.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+
+        // THEN calling intercept returns true
+        mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null);
+
+        // THEN the returned intent is original intent
+        assertSame(originalIntent, mInterceptor.mIntent);
+    }
+
+    @Test
+    public void testLockedManagedProfileShowWhenLockedEncryptedStorage() {
+        // GIVEN that the user storage is locked, activity has showWhenLocked flag but no
+        // directBootAware flag
+        when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+        when(mUserManager.isUserUnlocked(eq(TEST_USER_ID))).thenReturn(false);
+        mAInfo.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+        mAInfo.directBootAware = false;
+
+        // THEN calling intercept returns true
+        mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
+
+        // THEN the returned intent is the confirm credentials intent
+        assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
+    public void testLockedManagedProfileShowWhenLockedEncryptedStorageDirectBootAware() {
+        Intent originalIntent = new Intent();
+        // GIVEN that the user storage is locked, activity has showWhenLocked flag and
+        // directBootAware flag
+        when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+        when(mUserManager.isUserUnlocked(eq(TEST_USER_ID))).thenReturn(false);
+        mAInfo.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+        mAInfo.directBootAware = true;
+
+        // THEN calling intercept returns true
+        mInterceptor.intercept(originalIntent, null, mAInfo, null, null, null, 0, 0, null);
+
+        // THEN the returned intent is original intent
+        assertSame(originalIntent, mInterceptor.mIntent);
+    }
+
+    @Test
     public void testHarmfulAppWarning() throws RemoteException {
         // GIVEN the package we're about to launch has a harmful app warning set
         when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c898119..cdb2642 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -443,6 +443,44 @@
     }
 
     @Test
+    public void testUpdateAboveInsetsState_imeTargetOnScreenBehavior() {
+        final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent);
+        final WindowState ime = createWindow(null,  TYPE_INPUT_METHOD, imeToken, "ime");
+        final WindowState app = createTestWindow("app");
+
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
+        ime.getControllableInsetProvider().setServerVisible(true);
+
+        app.mActivityRecord.setVisibility(true);
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.updateImeInputAndControlTarget(app);
+
+        app.setRequestedVisibleTypes(ime(), ime());
+        getController().onInsetsModified(app);
+        assertTrue(ime.getControllableInsetProvider().getSource().isVisible());
+
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+        assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+        verify(app, atLeastOnce()).notifyInsetsChanged();
+
+        // Expect the app will still get IME insets even when the app was invisible.
+        // (i.e. app invisible after locking the device)
+        app.mActivityRecord.setVisible(false);
+        app.setHasSurface(false);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+        assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+        verify(app, atLeastOnce()).notifyInsetsChanged();
+
+        // Expect the app will get IME insets when the app is requesting visible.
+        // (i.e. app is going to visible when unlocking the device)
+        app.mActivityRecord.setVisibility(true);
+        assertTrue(app.isVisibleRequested());
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+        assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+        verify(app, atLeastOnce()).notifyInsetsChanged();
+    }
+
+    @Test
     public void testDispatchGlobalInsets() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index c548dc3..eb26415 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -772,6 +772,7 @@
             // Simulating now win1 is being covered by the lockscreen which has no surface,
             // and then launching an activity win2 with the remote animation
             win1.mHasSurface = false;
+            win1.mActivityRecord.setVisibility(false);
             mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
                     win2.mActivityRecord, new Point(50, 100), null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index d3aa073..df7b3cd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -74,15 +74,15 @@
 
         int id = startSyncSet(bse, listener);
         bse.addToSyncSet(id, mockWC);
-        // Make sure a traversal is requested
-        verify(mWm.mWindowPlacerLocked, times(1)).requestTraversal();
+        // The traversal is not requested because ready is not set.
+        verify(mWm.mWindowPlacerLocked, times(0)).requestTraversal();
 
         bse.onSurfacePlacement();
         verify(listener, times(0)).onTransactionReady(anyInt(), any());
 
         bse.setReady(id);
         // Make sure a traversal is requested
-        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+        verify(mWm.mWindowPlacerLocked).requestTraversal();
         bse.onSurfacePlacement();
         verify(listener, times(1)).onTransactionReady(eq(id), notNull());
 
@@ -103,14 +103,14 @@
         int id = startSyncSet(bse, listener);
         bse.addToSyncSet(id, mockWC);
         bse.setReady(id);
-        // Make sure traversals requested (one for add and another for setReady)
-        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+        // Make sure traversals requested.
+        verify(mWm.mWindowPlacerLocked).requestTraversal();
         bse.onSurfacePlacement();
         verify(listener, times(0)).onTransactionReady(anyInt(), any());
 
         mockWC.onSyncFinishedDrawing();
-        // Make sure a (third) traversal is requested.
-        verify(mWm.mWindowPlacerLocked, times(3)).requestTraversal();
+        // Make sure the second traversal is requested.
+        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
         bse.onSurfacePlacement();
         verify(listener, times(1)).onTransactionReady(eq(id), notNull());
     }
@@ -127,8 +127,8 @@
         int id = startSyncSet(bse, listener);
         bse.addToSyncSet(id, mockWC);
         bse.setReady(id);
-        // Make sure traversals requested (one for add and another for setReady)
-        verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+        // Make sure traversals requested.
+        verify(mWm.mWindowPlacerLocked).requestTraversal();
         bse.onSurfacePlacement();
         verify(listener, times(0)).onTransactionReady(anyInt(), any());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index a4cc09a..e7813ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -893,11 +893,10 @@
     }
 
     @Test
-    public void testLaunchesPortraitUnresizableOnFreeformLandscapeDisplay() {
+    public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() {
         mAtm.mDevEnableNonResizableMultiWindow = true;
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
-        assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
         final ActivityOptions options = ActivityOptions.makeBasic();
         mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
         mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
@@ -905,42 +904,12 @@
         assertEquals(RESULT_CONTINUE,
                 new CalculateRequestBuilder().setOptions(options).calculate());
 
-        assertEquals(WINDOWING_MODE_UNDEFINED, mResult.mWindowingMode);
-    }
-
-    @Test
-    public void testLaunchesLandscapeUnresizableOnFreeformLandscapeDisplay() {
-        mAtm.mDevEnableNonResizableMultiWindow = true;
-        final TestDisplayContent freeformDisplay = createNewDisplayContent(
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
                 WINDOWING_MODE_FREEFORM);
-        assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
-        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-        mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
-        assertEquals(RESULT_CONTINUE,
-                new CalculateRequestBuilder().setOptions(options).calculate());
-
-        assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
     }
 
     @Test
-    public void testLaunchesUndefinedUnresizableOnFreeformLandscapeDisplay() {
-        mAtm.mDevEnableNonResizableMultiWindow = true;
-        final TestDisplayContent freeformDisplay = createNewDisplayContent(
-                WINDOWING_MODE_FREEFORM);
-        assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
-        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-        assertEquals(RESULT_CONTINUE,
-                new CalculateRequestBuilder().setOptions(options).calculate());
-
-        assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
-    }
-
-    @Test
-    public void testForceMaximizingAppsOnNonFreeformDisplay() {
+    public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() {
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
         options.setLaunchBounds(new Rect(0, 0, 200, 100));
@@ -954,9 +923,8 @@
         assertEquals(RESULT_CONTINUE,
                 new CalculateRequestBuilder().setOptions(options).calculate());
 
-        // Non-resizable apps must be launched in fullscreen in a fullscreen display regardless of
-        // other properties.
-        assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 35b9710..59a31b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -479,6 +479,8 @@
         wallpaperWindow.mHasSurface = true;
         doReturn(true).when(mDisplayContent).isAttached();
         transition.collect(mDisplayContent);
+        assertFalse("The change of non-interesting window container should be skipped",
+                transition.mChanges.containsKey(mDisplayContent.getParent()));
         mDisplayContent.getWindowConfiguration().setRotation(
                 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
similarity index 65%
rename from services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
rename to services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
index d5eea1f..7657454 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
@@ -17,16 +17,16 @@
 package com.android.server.voiceinteraction;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.service.voice.HotwordAudioStream.KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES;
 
 import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG;
 
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
-import android.media.permission.Identity;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.service.voice.HotwordAudioStream;
 import android.service.voice.HotwordDetectedResult;
-import android.util.Pair;
 import android.util.Slog;
 
 import java.io.IOException;
@@ -39,21 +39,38 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-final class HotwordAudioStreamManager {
+/**
+ * Copies the audio streams in {@link HotwordDetectedResult}s. This allows the system to manage the
+ * lifetime of the {@link ParcelFileDescriptor}s and ensures that the flow of data is in the right
+ * direction from the {@link android.service.voice.HotwordDetectionService} to the client (i.e., the
+ * voice interactor).
+ *
+ * @hide
+ */
+final class HotwordAudioStreamCopier {
 
-    private static final String TAG = "HotwordAudioStreamManager";
+    private static final String TAG = "HotwordAudioStreamCopier";
     private static final String OP_MESSAGE = "Streaming hotword audio to VoiceInteractionService";
     private static final String TASK_ID_PREFIX = "HotwordDetectedResult@";
     private static final String THREAD_NAME_PREFIX = "Copy-";
+    private static final int DEFAULT_COPY_BUFFER_LENGTH_BYTES = 2_560;
+
+    // Corresponds to the OS pipe capacity in bytes
+    private static final int MAX_COPY_BUFFER_LENGTH_BYTES = 65_536;
 
     private final AppOpsManager mAppOpsManager;
-    private final Identity mVoiceInteractorIdentity;
+    private final int mVoiceInteractorUid;
+    private final String mVoiceInteractorPackageName;
+    private final String mVoiceInteractorAttributionTag;
     private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
 
-    HotwordAudioStreamManager(@NonNull AppOpsManager appOpsManager,
-            @NonNull Identity voiceInteractorIdentity) {
+    HotwordAudioStreamCopier(@NonNull AppOpsManager appOpsManager,
+            int voiceInteractorUid, @NonNull String voiceInteractorPackageName,
+            @NonNull String voiceInteractorAttributionTag) {
         mAppOpsManager = appOpsManager;
-        mVoiceInteractorIdentity = voiceInteractorIdentity;
+        mVoiceInteractorUid = voiceInteractorUid;
+        mVoiceInteractorPackageName = voiceInteractorPackageName;
+        mVoiceInteractorAttributionTag = voiceInteractorAttributionTag;
     }
 
     /**
@@ -61,7 +78,7 @@
      * <p>
      * The returned {@link HotwordDetectedResult} is identical the one that was passed in, except
      * that the {@link ParcelFileDescriptor}s within {@link HotwordDetectedResult#getAudioStreams()}
-     * are replaced with descriptors from pipes managed by {@link HotwordAudioStreamManager}. The
+     * are replaced with descriptors from pipes managed by {@link HotwordAudioStreamCopier}. The
      * returned value should be passed on to the client (i.e., the voice interactor).
      * </p>
      *
@@ -76,8 +93,7 @@
         }
 
         List<HotwordAudioStream> newAudioStreams = new ArrayList<>(audioStreams.size());
-        List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks = new ArrayList<>(
-                audioStreams.size());
+        List<CopyTaskInfo> copyTaskInfos = new ArrayList<>(audioStreams.size());
         for (HotwordAudioStream audioStream : audioStreams) {
             ParcelFileDescriptor[] clientPipe = ParcelFileDescriptor.createReliablePipe();
             ParcelFileDescriptor clientAudioSource = clientPipe[0];
@@ -87,46 +103,69 @@
                             clientAudioSource).build();
             newAudioStreams.add(newAudioStream);
 
+            int copyBufferLength = DEFAULT_COPY_BUFFER_LENGTH_BYTES;
+            PersistableBundle metadata = audioStream.getMetadata();
+            if (metadata.containsKey(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES)) {
+                copyBufferLength = metadata.getInt(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES, -1);
+                if (copyBufferLength < 1 || copyBufferLength > MAX_COPY_BUFFER_LENGTH_BYTES) {
+                    Slog.w(TAG, "Attempted to set an invalid copy buffer length ("
+                            + copyBufferLength + ") for: " + audioStream);
+                    copyBufferLength = DEFAULT_COPY_BUFFER_LENGTH_BYTES;
+                } else if (DEBUG) {
+                    Slog.i(TAG, "Copy buffer length set to " + copyBufferLength + " for: "
+                            + audioStream);
+                }
+            }
+
             ParcelFileDescriptor serviceAudioSource =
                     audioStream.getAudioStreamParcelFileDescriptor();
-            sourcesAndSinks.add(new Pair<>(serviceAudioSource, clientAudioSink));
+            copyTaskInfos.add(new CopyTaskInfo(serviceAudioSource, clientAudioSink,
+                    copyBufferLength));
         }
 
         String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result);
-        mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, sourcesAndSinks));
+        mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, copyTaskInfos));
 
         return result.buildUpon().setAudioStreams(newAudioStreams).build();
     }
 
+    private static class CopyTaskInfo {
+        private final ParcelFileDescriptor mSource;
+        private final ParcelFileDescriptor mSink;
+        private final int mCopyBufferLength;
+
+        CopyTaskInfo(ParcelFileDescriptor source, ParcelFileDescriptor sink, int copyBufferLength) {
+            mSource = source;
+            mSink = sink;
+            mCopyBufferLength = copyBufferLength;
+        }
+    }
+
     private class HotwordDetectedResultCopyTask implements Runnable {
         private final String mResultTaskId;
-        private final List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> mSourcesAndSinks;
+        private final List<CopyTaskInfo> mCopyTaskInfos;
         private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
 
-        HotwordDetectedResultCopyTask(String resultTaskId,
-                List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks) {
+        HotwordDetectedResultCopyTask(String resultTaskId, List<CopyTaskInfo> copyTaskInfos) {
             mResultTaskId = resultTaskId;
-            mSourcesAndSinks = sourcesAndSinks;
+            mCopyTaskInfos = copyTaskInfos;
         }
 
         @Override
         public void run() {
             Thread.currentThread().setName(THREAD_NAME_PREFIX + mResultTaskId);
-            int size = mSourcesAndSinks.size();
+            int size = mCopyTaskInfos.size();
             List<SingleAudioStreamCopyTask> tasks = new ArrayList<>(size);
             for (int i = 0; i < size; i++) {
-                Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink =
-                        mSourcesAndSinks.get(i);
-                ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
-                ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
+                CopyTaskInfo copyTaskInfo = mCopyTaskInfos.get(i);
                 String streamTaskId = mResultTaskId + "@" + i;
-                tasks.add(new SingleAudioStreamCopyTask(streamTaskId, serviceAudioSource,
-                        clientAudioSink));
+                tasks.add(new SingleAudioStreamCopyTask(streamTaskId, copyTaskInfo.mSource,
+                        copyTaskInfo.mSink, copyTaskInfo.mCopyBufferLength));
             }
 
             if (mAppOpsManager.startOpNoThrow(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
-                    mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
-                    mVoiceInteractorIdentity.attributionTag, OP_MESSAGE) == MODE_ALLOWED) {
+                    mVoiceInteractorUid, mVoiceInteractorPackageName,
+                    mVoiceInteractorAttributionTag, OP_MESSAGE) == MODE_ALLOWED) {
                 try {
                     // TODO(b/244599891): Set timeout, close after inactivity
                     mExecutorService.invokeAll(tasks);
@@ -135,25 +174,23 @@
                     bestEffortPropagateError(e.getMessage());
                 } finally {
                     mAppOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
-                            mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
-                            mVoiceInteractorIdentity.attributionTag);
+                            mVoiceInteractorUid, mVoiceInteractorPackageName,
+                            mVoiceInteractorAttributionTag);
                 }
             } else {
                 bestEffortPropagateError(
-                        "Failed to obtain RECORD_AUDIO_HOTWORD permission for "
-                                + SoundTriggerSessionPermissionsDecorator.toString(
-                                mVoiceInteractorIdentity));
+                        "Failed to obtain RECORD_AUDIO_HOTWORD permission for voice interactor with"
+                                + " uid=" + mVoiceInteractorUid
+                                + " packageName=" + mVoiceInteractorPackageName
+                                + " attributionTag=" + mVoiceInteractorAttributionTag);
             }
         }
 
         private void bestEffortPropagateError(@NonNull String errorMessage) {
             try {
-                for (Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink :
-                        mSourcesAndSinks) {
-                    ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
-                    ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
-                    serviceAudioSource.closeWithError(errorMessage);
-                    clientAudioSink.closeWithError(errorMessage);
+                for (CopyTaskInfo copyTaskInfo : mCopyTaskInfos) {
+                    copyTaskInfo.mSource.closeWithError(errorMessage);
+                    copyTaskInfo.mSink.closeWithError(errorMessage);
                 }
             } catch (IOException e) {
                 Slog.e(TAG, mResultTaskId + ": Failed to propagate error", e);
@@ -162,18 +199,17 @@
     }
 
     private static class SingleAudioStreamCopyTask implements Callable<Void> {
-        // TODO: Make this buffer size customizable from updateState()
-        private static final int COPY_BUFFER_LENGTH = 2_560;
-
         private final String mStreamTaskId;
         private final ParcelFileDescriptor mAudioSource;
         private final ParcelFileDescriptor mAudioSink;
+        private final int mCopyBufferLength;
 
         SingleAudioStreamCopyTask(String streamTaskId, ParcelFileDescriptor audioSource,
-                ParcelFileDescriptor audioSink) {
+                ParcelFileDescriptor audioSink, int copyBufferLength) {
             mStreamTaskId = streamTaskId;
             mAudioSource = audioSource;
             mAudioSink = audioSink;
+            mCopyBufferLength = copyBufferLength;
         }
 
         @Override
@@ -189,7 +225,7 @@
             try {
                 fis = new ParcelFileDescriptor.AutoCloseInputStream(mAudioSource);
                 fos = new ParcelFileDescriptor.AutoCloseOutputStream(mAudioSink);
-                byte[] buffer = new byte[COPY_BUFFER_LENGTH];
+                byte[] buffer = new byte[mCopyBufferLength];
                 while (true) {
                     if (Thread.interrupted()) {
                         Slog.e(TAG,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 4151663..3bcba6c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -183,7 +183,7 @@
     private final ScheduledExecutorService mScheduledExecutorService =
             Executors.newSingleThreadScheduledExecutor();
     private final AppOpsManager mAppOpsManager;
-    private final HotwordAudioStreamManager mHotwordAudioStreamManager;
+    private final HotwordAudioStreamCopier mHotwordAudioStreamCopier;
     @Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
     private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
     private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@@ -245,8 +245,9 @@
         mVoiceInteractionServiceUid = voiceInteractionServiceUid;
         mVoiceInteractorIdentity = voiceInteractorIdentity;
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
-        mHotwordAudioStreamManager = new HotwordAudioStreamManager(mAppOpsManager,
-                mVoiceInteractorIdentity);
+        mHotwordAudioStreamCopier = new HotwordAudioStreamCopier(mAppOpsManager,
+                mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+                mVoiceInteractorIdentity.attributionTag);
         mDetectionComponentName = serviceName;
         mUser = userId;
         mCallback = callback;
@@ -506,7 +507,7 @@
                     saveProximityValueToBundle(result);
                     HotwordDetectedResult newResult;
                     try {
-                        newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+                        newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
                     } catch (IOException e) {
                         // TODO: Write event
                         mSoftwareCallback.onError();
@@ -641,7 +642,7 @@
                     saveProximityValueToBundle(result);
                     HotwordDetectedResult newResult;
                     try {
-                        newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+                        newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
                     } catch (IOException e) {
                         // TODO: Write event
                         externalCallback.onError(CALLBACK_ONDETECTED_STREAM_COPY_ERROR);
@@ -1000,7 +1001,7 @@
                                     HotwordDetectedResult newResult;
                                     try {
                                         newResult =
-                                                mHotwordAudioStreamManager.startCopyingAudioStreams(
+                                                mHotwordAudioStreamCopier.startCopyingAudioStreams(
                                                         triggerResult);
                                     } catch (IOException e) {
                                         // TODO: Write event
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java
index b87b8f7..eeafe91 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java
@@ -183,7 +183,7 @@
     private final ScheduledExecutorService mScheduledExecutorService =
             Executors.newSingleThreadScheduledExecutor();
     private final AppOpsManager mAppOpsManager;
-    private final HotwordAudioStreamManager mHotwordAudioStreamManager;
+    private final HotwordAudioStreamCopier mHotwordAudioStreamCopier;
     @Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
     private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
     private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@@ -245,8 +245,9 @@
         mVoiceInteractionServiceUid = voiceInteractionServiceUid;
         mVoiceInteractorIdentity = voiceInteractorIdentity;
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
-        mHotwordAudioStreamManager = new HotwordAudioStreamManager(mAppOpsManager,
-                mVoiceInteractorIdentity);
+        mHotwordAudioStreamCopier = new HotwordAudioStreamCopier(mAppOpsManager,
+                mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+                mVoiceInteractorIdentity.attributionTag);
         mDetectionComponentName = serviceName;
         mUser = userId;
         mCallback = callback;
@@ -506,7 +507,7 @@
                     saveProximityValueToBundle(result);
                     HotwordDetectedResult newResult;
                     try {
-                        newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+                        newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
                     } catch (IOException e) {
                         // TODO: Write event
                         mSoftwareCallback.onError();
@@ -641,7 +642,7 @@
                     saveProximityValueToBundle(result);
                     HotwordDetectedResult newResult;
                     try {
-                        newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+                        newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
                     } catch (IOException e) {
                         // TODO: Write event
                         externalCallback.onError(CALLBACK_ONDETECTED_STREAM_COPY_ERROR);
@@ -1000,7 +1001,7 @@
                                     HotwordDetectedResult newResult;
                                     try {
                                         newResult =
-                                                mHotwordAudioStreamManager.startCopyingAudioStreams(
+                                                mHotwordAudioStreamCopier.startCopyingAudioStreams(
                                                         triggerResult);
                                     } catch (IOException e) {
                                         // TODO: Write event
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
deleted file mode 100644
index 791e471..0000000
--- a/startop/view_compiler/TEST_MAPPING
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "dex-builder-test"
-    },
-    {
-      "name": "CtsViewTestCases",
-      "options": [
-        {
-          "include-filter": "android.view.cts.PrecompiledLayoutTest"
-        }
-      ]
-    }
-  ]
-}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index 8519173..ea4480d 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -65,6 +65,7 @@
     oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
             in byte[] pdu);
     oneway void acknowledgeSms(int token, int messageRef, int result);
+    oneway void acknowledgeSmsWithPdu(int token, int messageRef, int result, in byte[] pdu);
     oneway void acknowledgeSmsReport(int token, int messageRef, int result);
     String getSmsFormat();
     oneway void onSmsReady();
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 8147759..d776928 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -275,6 +275,12 @@
         }
 
         @Override
+        public void acknowledgeSmsWithPdu(int token, int messageRef, int result, byte[] pdu) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .acknowledgeSms(token, messageRef, result, pdu), "acknowledgeSms");
+        }
+
+        @Override
         public void acknowledgeSmsReport(int token, int messageRef, int result) {
             executeMethodAsyncNoException(() -> MmTelFeature.this
                     .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
@@ -1087,6 +1093,11 @@
         getSmsImplementation().acknowledgeSms(token, messageRef, result);
     }
 
+    private void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.DeliverStatusResult int result, byte[] pdu) {
+        getSmsImplementation().acknowledgeSms(token, messageRef, result, pdu);
+    }
+
     private void acknowledgeSmsReport(int token, int messageRef,
             @ImsSmsImplBase.StatusReportResult int result) {
         getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index fb997d1..66833d1 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.telephony.SmsManager;
@@ -174,6 +175,9 @@
      * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
      * provider.
      *
+     * If the framework needs to provide the PDU used to acknowledge the SMS,
+     * {@link #acknowledgeSms(int, int, int, byte[])} will be called.
+     *
      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
      * @param messageRef the message reference, which may be 1 byte if it is in
      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
@@ -186,6 +190,27 @@
     }
 
     /**
+     * This method will be called by the platform after
+     * {@link #onSmsReceived(int, String, byte[])} has been called to acknowledge an incoming SMS.
+     *
+     * This method is only called in cases where the framework needs to provide the PDU such as the
+     * case where we provide the Short Message Transfer Layer PDU (see 3GPP TS 23.040). Otherwise,
+     * {@link #acknowledgeSms(int, int, int)} will be used.
+     *
+     * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param result result of delivering the message.
+     * @param pdu PDU representing the contents of the message.
+     */
+    public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535)  int messageRef,
+            @DeliverStatusResult int result, @NonNull byte[] pdu) {
+        Log.e(LOG_TAG, "acknowledgeSms() not implemented. acknowledgeSms(int, int, int) called.");
+        acknowledgeSms(token, messageRef, result);
+    }
+
+    /**
      * This method will be triggered by the platform after
      * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
      * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 15a6afc..7c5dcf8 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -50,6 +50,7 @@
             "android.app.servertransaction.", // all tests under the package.
             "android.view.CutoutSpecificationTest",
             "android.view.DisplayCutoutTest",
+            "android.view.DisplayShapeTest",
             "android.view.InsetsAnimationControlImplTest",
             "android.view.InsetsControllerTest",
             "android.view.InsetsFlagsTest",
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index f7f2f22..d7a39bf 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -161,6 +161,15 @@
                                           << "failed shortening resource paths");
         return 1;
       }
+
+      if (options_.obfuscation_map_path &&
+          !obfuscator.WriteObfuscationMap(options_.obfuscation_map_path.value())) {
+        context_->GetDiagnostics()->Error(android::DiagMessage()
+                                          << "failed to write the obfuscation map to file");
+        return 1;
+      }
+
+      // TODO(b/246489170): keep the old option and format until transform to the new one
       if (options_.shortened_paths_map_path
           && !WriteShortenedPathsMap(options_.table_flattener_options.shortened_path_map,
                                       options_.shortened_paths_map_path.value())) {
@@ -292,6 +301,7 @@
                                         ArchiveEntry::kAlign, writer);
   }
 
+  // TODO(b/246489170): keep the old option and format until transform to the new one
   bool WriteShortenedPathsMap(const std::map<std::string, std::string> &path_map,
                                const std::string &file_path) {
     std::stringstream ss;
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 794a87b..1879f25 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -58,6 +58,7 @@
   bool shorten_resource_paths = false;
 
   // Path to the output map of original resource paths to shortened paths.
+  // TODO(b/246489170): keep the old option and format until transform to the new one
   std::optional<std::string> shortened_paths_map_path;
 
   // Whether sparse encoding should be used for O+ resources.
@@ -65,6 +66,9 @@
 
   // Whether sparse encoding should be used for all resources.
   bool force_sparse_encoding = false;
+
+  // Path to the output map of original resource paths/names to obfuscated paths/names.
+  std::optional<std::string> obfuscation_map_path;
 };
 
 class OptimizeCommand : public Command {
@@ -120,9 +124,13 @@
     AddOptionalSwitch("--shorten-resource-paths",
         "Shortens the paths of resources inside the APK.",
         &options_.shorten_resource_paths);
+    // TODO(b/246489170): keep the old option and format until transform to the new one
     AddOptionalFlag("--resource-path-shortening-map",
-        "Path to output the map of old resource paths to shortened paths.",
-        &options_.shortened_paths_map_path);
+                    "[Deprecated]Path to output the map of old resource paths to shortened paths.",
+                    &options_.shortened_paths_map_path);
+    AddOptionalFlag("--save-obfuscation-map",
+                    "Path to output the map of original paths/names to obfuscated paths/names.",
+                    &options_.obfuscation_map_path);
     AddOptionalSwitch(
         "--deduplicate-entry-values",
         "Whether to deduplicate pairs of resource entry and value for simple resources.\n"
diff --git a/tools/aapt2/optimize/Obfuscator.cpp b/tools/aapt2/optimize/Obfuscator.cpp
index 2533f80..cc21093 100644
--- a/tools/aapt2/optimize/Obfuscator.cpp
+++ b/tools/aapt2/optimize/Obfuscator.cpp
@@ -16,6 +16,7 @@
 
 #include "optimize/Obfuscator.h"
 
+#include <fstream>
 #include <map>
 #include <set>
 #include <string>
@@ -192,6 +193,29 @@
   return true;
 }
 
+bool Obfuscator::WriteObfuscationMap(const std::string& file_path) const {
+  pb::ResourceMappings resourceMappings;
+  for (const auto& [id, name] : options_.id_resource_map) {
+    auto* collapsedNameMapping = resourceMappings.mutable_collapsed_names()->add_resource_names();
+    collapsedNameMapping->set_id(id);
+    collapsedNameMapping->set_name(name);
+  }
+
+  for (const auto& [original_path, shortened_path] : options_.shortened_path_map) {
+    auto* resource_path = resourceMappings.mutable_shortened_paths()->add_resource_paths();
+    resource_path->set_original_path(original_path);
+    resource_path->set_shortened_path(shortened_path);
+  }
+
+  {  // RAII style, output the pb content to file and close fout in destructor
+    std::ofstream fout(file_path, std::ios::out | std::ios::trunc | std::ios::binary);
+    if (!fout.is_open()) {
+      return false;
+    }
+    return resourceMappings.SerializeToOstream(&fout);
+  }
+}
+
 /**
  * Tell the optimizer whether it's needed to dump information for de-obfuscating.
  *
diff --git a/tools/aapt2/optimize/Obfuscator.h b/tools/aapt2/optimize/Obfuscator.h
index 786bf8c..5ccf5438 100644
--- a/tools/aapt2/optimize/Obfuscator.h
+++ b/tools/aapt2/optimize/Obfuscator.h
@@ -20,6 +20,7 @@
 #include <set>
 #include <string>
 
+#include "ResourceMetadata.pb.h"
 #include "ResourceTable.h"
 #include "android-base/function_ref.h"
 #include "android-base/macros.h"
@@ -38,6 +39,8 @@
 
   bool Consume(IAaptContext* context, ResourceTable* table) override;
 
+  bool WriteObfuscationMap(const std::string& file_path) const;
+
   bool IsEnabled() const;
 
   enum class Result { Obfuscated, Keep_ExemptionList, Keep_Overlayable };
diff --git a/tools/aapt2/optimize/Obfuscator_test.cpp b/tools/aapt2/optimize/Obfuscator_test.cpp
index 17d1e52..7f57b71 100644
--- a/tools/aapt2/optimize/Obfuscator_test.cpp
+++ b/tools/aapt2/optimize/Obfuscator_test.cpp
@@ -21,10 +21,14 @@
 #include <string>
 
 #include "ResourceTable.h"
+#include "android-base/file.h"
 #include "test/Test.h"
 
 using ::aapt::test::GetValue;
+using ::testing::AnyOf;
 using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::IsTrue;
 using ::testing::Not;
 using ::testing::NotNull;
 
@@ -236,4 +240,60 @@
   ASSERT_THAT(obfuscatorWithCollapseStringPoolOption.IsEnabled(), Eq(true));
 }
 
+static std::unique_ptr<ResourceTable> getProtocolBufferTableUnderTest() {
+  std::string original_xml_path = "res/drawable/xmlfile.xml";
+  std::string original_png_path = "res/drawable/pngfile.png";
+
+  return test::ResourceTableBuilder()
+      .AddFileReference("com.app.test:drawable/xmlfile", original_xml_path)
+      .AddFileReference("com.app.test:drawable/pngfile", original_png_path)
+      .AddValue("com.app.test:color/mycolor", aapt::ResourceId(0x7f020000),
+                aapt::util::make_unique<aapt::BinaryPrimitive>(
+                    uint8_t(android::Res_value::TYPE_INT_COLOR_ARGB8), 0xffaabbcc))
+      .AddString("com.app.test:string/mystring", ResourceId(0x7f030000), "hello world")
+      .Build();
+}
+
+TEST(ObfuscatorTest, WriteObfuscationMapInProtocolBufferFormat) {
+  OptimizeOptions options{.shorten_resource_paths = true};
+  options.table_flattener_options.collapse_key_stringpool = true;
+  Obfuscator obfuscator(options);
+  ASSERT_TRUE(obfuscator.Consume(test::ContextBuilder().Build().get(),
+                                 getProtocolBufferTableUnderTest().get()));
+
+  obfuscator.WriteObfuscationMap("obfuscated_map.pb");
+
+  std::string pbOut;
+  android::base::ReadFileToString("obfuscated_map.pb", &pbOut, false /* follow_symlinks */);
+  EXPECT_THAT(pbOut, HasSubstr("drawable/xmlfile.xml"));
+  EXPECT_THAT(pbOut, HasSubstr("drawable/pngfile.png"));
+  EXPECT_THAT(pbOut, HasSubstr("mycolor"));
+  EXPECT_THAT(pbOut, HasSubstr("mystring"));
+  pb::ResourceMappings resourceMappings;
+  EXPECT_THAT(resourceMappings.ParseFromString(pbOut), IsTrue());
+  EXPECT_THAT(resourceMappings.collapsed_names().resource_names_size(), Eq(2));
+  auto& resource_names = resourceMappings.collapsed_names().resource_names();
+  EXPECT_THAT(resource_names.at(0).name(), AnyOf(Eq("mycolor"), Eq("mystring")));
+  EXPECT_THAT(resource_names.at(1).name(), AnyOf(Eq("mycolor"), Eq("mystring")));
+  auto& shortened_paths = resourceMappings.shortened_paths();
+  EXPECT_THAT(shortened_paths.resource_paths_size(), Eq(2));
+  EXPECT_THAT(shortened_paths.resource_paths(0).original_path(),
+              AnyOf(Eq("res/drawable/pngfile.png"), Eq("res/drawable/xmlfile.xml")));
+  EXPECT_THAT(shortened_paths.resource_paths(1).original_path(),
+              AnyOf(Eq("res/drawable/pngfile.png"), Eq("res/drawable/xmlfile.xml")));
+}
+
+TEST(ObfuscatorTest, WriteObfuscatingMapWithNonEnabledOption) {
+  OptimizeOptions options;
+  Obfuscator obfuscator(options);
+  ASSERT_TRUE(obfuscator.Consume(test::ContextBuilder().Build().get(),
+                                 getProtocolBufferTableUnderTest().get()));
+
+  obfuscator.WriteObfuscationMap("obfuscated_map.pb");
+
+  std::string pbOut;
+  android::base::ReadFileToString("obfuscated_map.pb", &pbOut, false /* follow_symlinks */);
+  ASSERT_THAT(pbOut, Eq(""));
+}
+
 }  // namespace aapt
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
index 720f835..0157596 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
@@ -17,6 +17,7 @@
 package com.google.android.lint
 
 import com.android.tools.lint.detector.api.getUMethod
+import org.jetbrains.uast.UAnnotation
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.UMethod
 import org.jetbrains.uast.UParameter
@@ -26,10 +27,11 @@
     return hasPermissionMethodAnnotation(method)
 }
 
-fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations
-        .any {
-            it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD)
-        }
+fun hasPermissionMethodAnnotation(method: UMethod): Boolean =
+        getPermissionMethodAnnotation(method) != null
+
+fun getPermissionMethodAnnotation(method: UMethod?): UAnnotation? = method?.uAnnotations
+        ?.firstOrNull { it.qualifiedName == ANNOTATION_PERMISSION_METHOD }
 
 fun hasPermissionNameAnnotation(parameter: UParameter) = parameter.annotations.any {
     it.hasQualifiedName(ANNOTATION_PERMISSION_NAME)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 413e197..c5cf0fb 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -40,7 +40,7 @@
         EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
         EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
         EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
-        SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+        SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
         SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
         PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
         RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG,
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index b377d50..a20266a 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -31,7 +31,7 @@
             EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
             EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
             EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
-            SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+            SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
     )
 
     override val api: Int
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index f1b6348..ee7dd62 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -17,10 +17,14 @@
 package com.google.android.lint.aidl
 
 import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
 import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.UastLintUtils.Companion.getAnnotationBooleanValue
 import com.android.tools.lint.detector.api.getUMethod
+import com.google.android.lint.getPermissionMethodAnnotation
 import com.google.android.lint.hasPermissionNameAnnotation
 import com.google.android.lint.isPermissionMethodCall
+import com.intellij.psi.PsiType
 import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.evaluateString
@@ -36,13 +40,37 @@
  */
 data class EnforcePermissionFix(
     val locations: List<Location>,
-    val permissionNames: List<String>
+    val permissionNames: List<String>,
+    val errorLevel: Boolean,
 ) {
-    val annotation: String
+    fun toLintFix(annotationLocation: Location): LintFix {
+        val removeFixes = this.locations.map {
+            LintFix.create()
+                .replace()
+                .reformat(true)
+                .range(it)
+                .with("")
+                .autoFix()
+                .build()
+        }
+
+        val annotateFix = LintFix.create()
+            .annotate(this.annotation)
+            .range(annotationLocation)
+            .autoFix()
+            .build()
+
+        return LintFix.create().composite(annotateFix, *removeFixes.toTypedArray())
+    }
+
+    private val annotation: String
         get() {
             val quotedPermissions = permissionNames.joinToString(", ") { """"$it"""" }
+
             val annotationParameter =
-                if (permissionNames.size > 1) "allOf={$quotedPermissions}" else quotedPermissions
+                if (permissionNames.size > 1) "allOf={$quotedPermissions}"
+                else quotedPermissions
+
             return "@$ANNOTATION_ENFORCE_PERMISSION($annotationParameter)"
         }
 
@@ -54,19 +82,28 @@
         fun fromCallExpression(
             context: JavaContext,
             callExpression: UCallExpression
-        ): EnforcePermissionFix? =
-            if (isPermissionMethodCall(callExpression)) {
-                EnforcePermissionFix(
+        ): EnforcePermissionFix? {
+            val method = callExpression.resolve()?.getUMethod() ?: return null
+            val annotation = getPermissionMethodAnnotation(method) ?: return null
+            val enforces = method.returnType == PsiType.VOID
+            val orSelf = getAnnotationBooleanValue(annotation, "orSelf") ?: false
+            return EnforcePermissionFix(
                     listOf(getPermissionCheckLocation(context, callExpression)),
-                    getPermissionCheckValues(callExpression)
-                )
-            } else null
+                    getPermissionCheckValues(callExpression),
+                    // If we detect that the PermissionMethod enforces that permission is granted,
+                    // AND is of the "orSelf" variety, we are very confident that this is a behavior
+                    // preserving migration to @EnforcePermission.  Thus, the incident should be ERROR
+                    // level.
+                    errorLevel = enforces && orSelf
+            )
+        }
 
 
         fun compose(individuals: List<EnforcePermissionFix>): EnforcePermissionFix =
             EnforcePermissionFix(
                 individuals.flatMap { it.locations },
-                individuals.flatMap { it.permissionNames }
+                individuals.flatMap { it.permissionNames },
+                errorLevel = individuals.all(EnforcePermissionFix::errorLevel)
             )
 
         /**
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
index 4c0cbe7..9999a0b 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
@@ -18,6 +18,7 @@
 
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
 import com.android.tools.lint.detector.api.Issue
 import com.android.tools.lint.detector.api.JavaContext
 import com.android.tools.lint.detector.api.Scope
@@ -28,6 +29,7 @@
 import org.jetbrains.uast.UIfExpression
 import org.jetbrains.uast.UMethod
 import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.skipParenthesizedExprDown
 
 /**
  * Looks for methods implementing generated AIDL interface stubs
@@ -44,30 +46,25 @@
             interfaceName: String,
             body: UBlockExpression
     ) {
-        val fix = accumulateSimplePermissionCheckFixes(body, context) ?: return
+        val enforcePermissionFix = accumulateSimplePermissionCheckFixes(body, context) ?: return
+        val lintFix = enforcePermissionFix.toLintFix(context.getLocation(node))
+        val message =
+                "$interfaceName permission check ${
+                    if (enforcePermissionFix.errorLevel) "should" else "can"
+                } be converted to @EnforcePermission annotation"
 
-        val javaRemoveFixes = fix.locations.map {
-            fix()
-                    .replace()
-                    .reformat(true)
-                    .range(it)
-                    .with("")
-                    .autoFix()
-                    .build()
+        val incident = Incident(
+                ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
+                enforcePermissionFix.locations.last(),
+                message,
+                lintFix
+        )
+
+        if (enforcePermissionFix.errorLevel) {
+            incident.overrideSeverity(Severity.ERROR)
         }
 
-        val javaAnnotateFix = fix()
-                .annotate(fix.annotation)
-                .range(context.getLocation(node))
-                .autoFix()
-                .build()
-
-        context.report(
-                ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
-                fix.locations.last(),
-                "$interfaceName permission check can be converted to @EnforcePermission annotation",
-                fix().composite(*javaRemoveFixes.toTypedArray(), javaAnnotateFix)
-        )
+        context.report(incident)
     }
 
     /**
@@ -89,7 +86,8 @@
             EnforcePermissionFix? {
         val singleFixes = mutableListOf<EnforcePermissionFix>()
         for (expression in methodBody.expressions) {
-            singleFixes.add(getPermissionCheckFix(expression, context) ?: break)
+            singleFixes.add(getPermissionCheckFix(expression.skipParenthesizedExprDown(), context)
+                    ?: break)
         }
         return when (singleFixes.size) {
             0 -> null
@@ -133,7 +131,7 @@
         """.trimIndent()
 
         @JvmField
-        val ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION = Issue.create(
+        val ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT = Issue.create(
                 id = "SimpleManualPermissionEnforcement",
                 briefDescription = "Manual permission check can be @EnforcePermission annotation",
                 explanation = EXPLANATION,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
index 150fc26..bdf9c89 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
@@ -18,7 +18,6 @@
 
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
 import com.android.tools.lint.checks.infrastructure.TestLintTask
-import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 
@@ -27,7 +26,7 @@
     override fun getDetector(): Detector = SimpleManualPermissionEnforcementDetector()
     override fun getIssues(): List<Issue> = listOf(
             SimpleManualPermissionEnforcementDetector
-            .ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION
+            .ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT
     )
 
     override fun lint(): TestLintTask = super.lint().allowMissingSdk()
@@ -36,15 +35,15 @@
         lint().files(
             java(
                 """
-                    import android.content.Context;
-                    import android.test.ITest;
-                    public class Foo extends ITest.Stub {
-                        private Context mContext;
-                        @Override
-                        public void test() throws android.os.RemoteException {
-                            mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
-                        }
+                import android.content.Context;
+                import android.test.ITest;
+                public class Foo extends ITest.Stub {
+                    private Context mContext;
+                    @Override
+                    public void test() throws android.os.RemoteException {
+                        mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
                     }
+                }
                 """
             ).indented(),
             *stubs
@@ -52,10 +51,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:7: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                         mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
@@ -69,22 +68,96 @@
             )
     }
 
+    fun testClass_orSelfFalse_warning() {
+        lint().files(
+                java(
+                    """
+                    import android.content.Context;
+                    import android.test.ITest;
+                    public class Foo extends ITest.Stub {
+                        private Context mContext;
+                        @Override
+                        public void test() throws android.os.RemoteException {
+                            mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                    """
+                    src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                            mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                    0 errors, 1 warnings
+                    """
+                )
+                .expectFixDiffs(
+                    """
+                    Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+                    @@ -5 +5
+                    +     @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+                    @@ -7 +8
+                    -         mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+                    """
+                )
+    }
+
+    fun testClass_enforcesFalse_warning() {
+        lint().files(
+                java(
+                    """
+                    import android.content.Context;
+                    import android.test.ITest;
+                    public class Foo extends ITest.Stub {
+                        private Context mContext;
+                        @Override
+                        public void test() throws android.os.RemoteException {
+                            mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+                        }
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                    """
+                    src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                            mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                    0 errors, 1 warnings
+                    """
+                )
+                .expectFixDiffs(
+                    """
+                    Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+                    @@ -5 +5
+                    +     @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+                    @@ -7 +8
+                    -         mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+                    """
+                )
+    }
+
     fun testAnonClass() {
         lint().files(
             java(
                 """
-                    import android.content.Context;
-                    import android.test.ITest;
-                    public class Foo {
-                        private Context mContext;
-                        private ITest itest = new ITest.Stub() {
-                            @Override
-                            public void test() throws android.os.RemoteException {
-                                mContext.enforceCallingOrSelfPermission(
-                                    "android.permission.READ_CONTACTS", "foo");
-                            }
-                        };
-                    }
+                import android.content.Context;
+                import android.test.ITest;
+                public class Foo {
+                    private Context mContext;
+                    private ITest itest = new ITest.Stub() {
+                        @Override
+                        public void test() throws android.os.RemoteException {
+                            mContext.enforceCallingOrSelfPermission(
+                                "android.permission.READ_CONTACTS", "foo");
+                        }
+                    };
+                }
                 """
             ).indented(),
             *stubs
@@ -92,10 +165,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                             mContext.enforceCallingOrSelfPermission(
                             ^
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
@@ -114,16 +187,16 @@
         lint().files(
             java(
                 """
-                    import android.content.Context;
-                    import android.test.ITest;
+                import android.content.Context;
+                import android.test.ITest;
 
-                    public class Foo extends ITest.Stub {
-                        private Context mContext;
-                        @Override
-                        public void test() throws android.os.RemoteException {
-                            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
-                        }
+                public class Foo extends ITest.Stub {
+                    private Context mContext;
+                    @Override
+                    public void test() throws android.os.RemoteException {
+                        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
                     }
+                }
                 """
             ).indented(),
             *stubs,
@@ -132,10 +205,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
@@ -153,20 +226,20 @@
         lint().files(
             java(
                 """
-                    import android.content.Context;
-                    import android.test.ITest;
-                    public class Foo {
-                        private Context mContext;
-                        private ITest itest = new ITest.Stub() {
-                            @Override
-                            public void test() throws android.os.RemoteException {
-                                mContext.enforceCallingOrSelfPermission(
-                                    "android.permission.READ_CONTACTS", "foo");
-                                mContext.enforceCallingOrSelfPermission(
-                                    "android.permission.WRITE_CONTACTS", "foo");
-                            }
-                        };
-                    }
+                import android.content.Context;
+                import android.test.ITest;
+                public class Foo {
+                    private Context mContext;
+                    private ITest itest = new ITest.Stub() {
+                        @Override
+                        public void test() throws android.os.RemoteException {
+                            mContext.enforceCallingOrSelfPermission(
+                                "android.permission.READ_CONTACTS", "foo");
+                            mContext.enforceCallingOrSelfPermission(
+                                "android.permission.WRITE_CONTACTS", "foo");
+                        }
+                    };
+                }
                 """
             ).indented(),
             *stubs
@@ -174,10 +247,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:10: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                             mContext.enforceCallingOrSelfPermission(
                             ^
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
@@ -194,20 +267,110 @@
             )
     }
 
+    fun testAllOf_mixedOrSelf_warning() {
+        lint().files(
+                java(
+                    """
+                    import android.content.Context;
+                    import android.test.ITest;
+                    public class Foo {
+                        private Context mContext;
+                        private ITest itest = new ITest.Stub() {
+                            @Override
+                            public void test() throws android.os.RemoteException {
+                                mContext.enforceCallingOrSelfPermission(
+                                    "android.permission.READ_CONTACTS", "foo");
+                                mContext.enforceCallingPermission(
+                                    "android.permission.WRITE_CONTACTS", "foo");
+                            }
+                        };
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                    """
+                    src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                                mContext.enforceCallingPermission(
+                                ^
+                    0 errors, 1 warnings
+                    """
+                )
+                .expectFixDiffs(
+                    """
+                    Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+                    @@ -6 +6
+                    +         @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+                    @@ -8 +9
+                    -             mContext.enforceCallingOrSelfPermission(
+                    -                 "android.permission.READ_CONTACTS", "foo");
+                    -             mContext.enforceCallingPermission(
+                    -                 "android.permission.WRITE_CONTACTS", "foo");
+                    """
+                )
+    }
+
+    fun testAllOf_mixedEnforces_warning() {
+        lint().files(
+                java(
+                    """
+                    import android.content.Context;
+                    import android.test.ITest;
+                    public class Foo {
+                        private Context mContext;
+                        private ITest itest = new ITest.Stub() {
+                            @Override
+                            public void test() throws android.os.RemoteException {
+                                mContext.enforceCallingOrSelfPermission(
+                                    "android.permission.READ_CONTACTS", "foo");
+                                mContext.checkCallingOrSelfPermission(
+                                    "android.permission.WRITE_CONTACTS", "foo");
+                            }
+                        };
+                    }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                    """
+                    src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                                mContext.checkCallingOrSelfPermission(
+                                ^
+                    0 errors, 1 warnings
+                    """
+                )
+                .expectFixDiffs(
+                    """
+                    Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+                    @@ -6 +6
+                    +         @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+                    @@ -8 +9
+                    -             mContext.enforceCallingOrSelfPermission(
+                    -                 "android.permission.READ_CONTACTS", "foo");
+                    -             mContext.checkCallingOrSelfPermission(
+                    -                 "android.permission.WRITE_CONTACTS", "foo");
+                    """
+                )
+    }
+
     fun testPrecedingExpressions() {
         lint().files(
             java(
                 """
-                    import android.os.Binder;
-                    import android.test.ITest;
-                    public class Foo extends ITest.Stub {
-                        private mContext Context;
-                        @Override
-                        public void test() throws android.os.RemoteException {
-                            long uid = Binder.getCallingUid();
-                            mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
-                        }
+                import android.os.Binder;
+                import android.test.ITest;
+                public class Foo extends ITest.Stub {
+                    private mContext Context;
+                    @Override
+                    public void test() throws android.os.RemoteException {
+                        long uid = Binder.getCallingUid();
+                        mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
                     }
+                }
                 """
             ).indented(),
             *stubs
@@ -217,25 +380,25 @@
     }
 
     fun testPermissionHelper() {
-        lint().skipTestModes(TestMode.PARENTHESIZED).files(
+        lint().files(
             java(
                 """
-                    import android.content.Context;
-                    import android.test.ITest;
+                import android.content.Context;
+                import android.test.ITest;
 
-                    public class Foo extends ITest.Stub {
-                        private Context mContext;
+                public class Foo extends ITest.Stub {
+                    private Context mContext;
 
-                        @android.content.pm.PermissionMethod
-                        private void helper() {
-                            mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
-                        }
-
-                        @Override
-                        public void test() throws android.os.RemoteException {
-                            helper();
-                        }
+                    @android.content.pm.PermissionMethod(orSelf = true)
+                    private void helper() {
+                        mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
                     }
+
+                    @Override
+                    public void test() throws android.os.RemoteException {
+                        helper();
+                    }
+                }
                 """
             ).indented(),
             *stubs
@@ -243,10 +406,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:14: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                         helper();
                         ~~~~~~~~~
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
@@ -260,8 +423,52 @@
             )
     }
 
+    fun testPermissionHelper_orSelfNotBubbledUp_warning() {
+        lint().files(
+                java(
+                    """
+                    import android.content.Context;
+                    import android.test.ITest;
+
+                    public class Foo extends ITest.Stub {
+                        private Context mContext;
+
+                        @android.content.pm.PermissionMethod
+                    private void helper() {
+                        mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+                    }
+
+                    @Override
+                    public void test() throws android.os.RemoteException {
+                        helper();
+                    }
+                }
+                    """
+                ).indented(),
+                *stubs
+        )
+                .run()
+                .expect(
+                    """
+                    src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                            helper();
+                            ~~~~~~~~~
+                    0 errors, 1 warnings
+                    """
+                )
+                .expectFixDiffs(
+                    """
+                    Fix for src/Foo.java line 14: Annotate with @EnforcePermission:
+                    @@ -12 +12
+                    +     @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+                    @@ -14 +15
+                    -         helper();
+                    """
+                )
+    }
+
     fun testPermissionHelperAllOf() {
-        lint().skipTestModes(TestMode.PARENTHESIZED).files(
+        lint().files(
             java(
                 """
                 import android.content.Context;
@@ -270,7 +477,7 @@
                 public class Foo extends ITest.Stub {
                     private Context mContext;
 
-                    @android.content.pm.PermissionMethod
+                    @android.content.pm.PermissionMethod(orSelf = true)
                     private void helper() {
                         mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
                         mContext.enforceCallingOrSelfPermission("android.permission.WRITE_CONTACTS", "foo");
@@ -289,10 +496,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:16: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:16: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                         mContext.enforceCallingOrSelfPermission("FOO", "foo");
                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
@@ -309,7 +516,7 @@
 
 
     fun testPermissionHelperNested() {
-        lint().skipTestModes(TestMode.PARENTHESIZED).files(
+        lint().files(
             java(
                 """
                 import android.content.Context;
@@ -318,12 +525,12 @@
                 public class Foo extends ITest.Stub {
                     private Context mContext;
 
-                    @android.content.pm.PermissionMethod
+                    @android.content.pm.PermissionMethod(orSelf = true)
                     private void helperHelper() {
                         helper("android.permission.WRITE_CONTACTS");
                     }
 
-                    @android.content.pm.PermissionMethod
+                    @android.content.pm.PermissionMethod(orSelf = true)
                     private void helper(@android.content.pm.PermissionName String extraPermission) {
                         mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
                     }
@@ -340,10 +547,10 @@
             .run()
             .expect(
                 """
-                src/Foo.java:19: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+                src/Foo.java:19: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
                         helperHelper();
                         ~~~~~~~~~~~~~~~
-                0 errors, 1 warnings
+                1 errors, 0 warnings
                 """
             )
             .expectFixDiffs(
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
index bd6b195..5ac8a0b 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
@@ -17,8 +17,12 @@
     """
         package android.content;
         public class Context {
-            @android.content.pm.PermissionMethod
+            @android.content.pm.PermissionMethod(orSelf = true)
             public void enforceCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
+            @android.content.pm.PermissionMethod
+            public void enforceCallingPermission(@android.content.pm.PermissionName String permission, String message) {}
+            @android.content.pm.PermissionMethod(orSelf = true)
+            public int checkCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
         }
     """
 ).indented()