Merge "CDM Transport clean-up" into udc-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 11c13c4..f6c18d6 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1447,7 +1447,6 @@
          * reasonable estimates should use the sentinel value
          * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
          * </ul>
-         * TODO(255371817): update documentation to reflect how this data will be used
          * Note that the system may choose to delay jobs with large network
          * usage estimates when the device has a poor network connection, in
          * order to save battery and possible network costs.
@@ -1478,6 +1477,7 @@
          * @see JobInfo#getEstimatedNetworkUploadBytes()
          * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
          */
+        // TODO(b/255371817): update documentation to reflect how this data will be used
         public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
                 @BytesLong long uploadBytes) {
             mNetworkDownloadBytes = downloadBytes;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4088a48..bf3789f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1476,7 +1476,8 @@
                     /* timingDelayConstraintSatisfied */ false,
                     /* isDeviceIdle */ false,
                     /* hasConnectivityConstraintSatisfied */ false,
-                    /* hasContentTriggerConstraintSatisfied */ false);
+                    /* hasContentTriggerConstraintSatisfied */ false,
+                    0);
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -1894,7 +1895,8 @@
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
-                    cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER));
+                    cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
+                    0);
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index dcfadca..29ab455 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -461,7 +461,8 @@
                     job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
                     job.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                     job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
-                    job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER));
+                    job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
+                    mExecutionStartTimeElapsed - job.enqueueTime);
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
                 // Use the context's ID to distinguish traces since there'll only be one job
                 // running per context.
@@ -1356,7 +1357,8 @@
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
                 completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
-                completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER));
+                completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
+                0);
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     getId());
diff --git a/core/api/current.txt b/core/api/current.txt
index acf030a..2161186 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5279,8 +5279,8 @@
   }
 
   public class BroadcastOptions {
+    ctor public BroadcastOptions();
     method public boolean isShareIdentityEnabled();
-    method @NonNull public static android.app.BroadcastOptions makeBasic();
     method @NonNull public android.app.BroadcastOptions setShareIdentityEnabled(boolean);
     method @NonNull public android.os.Bundle toBundle();
   }
@@ -13577,17 +13577,26 @@
   }
 
   public final class CreateCredentialRequest implements android.os.Parcelable {
-    ctor public CreateCredentialRequest(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean, boolean);
     method public boolean alwaysSendAppInfoToProvider();
     method public int describeContents();
     method @NonNull public android.os.Bundle getCandidateQueryData();
     method @NonNull public android.os.Bundle getCredentialData();
+    method @Nullable public String getOrigin();
     method @NonNull public String getType();
     method public boolean isSystemProviderRequired();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CreateCredentialRequest> CREATOR;
   }
 
+  public static final class CreateCredentialRequest.Builder {
+    ctor public CreateCredentialRequest.Builder(@NonNull android.os.Bundle, @NonNull android.os.Bundle);
+    method @NonNull public android.credentials.CreateCredentialRequest build();
+    method @NonNull public android.credentials.CreateCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean);
+    method @NonNull public android.credentials.CreateCredentialRequest.Builder setIsSystemProviderRequired(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public android.credentials.CreateCredentialRequest.Builder setOrigin(@NonNull String);
+    method @NonNull public android.credentials.CreateCredentialRequest.Builder setType(@NonNull String);
+  }
+
   public final class CreateCredentialResponse implements android.os.Parcelable {
     ctor public CreateCredentialResponse(@NonNull android.os.Bundle);
     method public int describeContents();
@@ -13619,9 +13628,7 @@
   public final class CredentialManager {
     method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
     method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public void createCredentialWithOrigin(@NonNull android.credentials.CreateCredentialRequest, @Nullable String, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
     method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public void getCredentialWithOrigin(@NonNull android.credentials.GetCredentialRequest, @Nullable String, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
     method public boolean isEnabledCredentialProviderService(@NonNull android.content.ComponentName);
     method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
     method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest);
@@ -13656,6 +13663,7 @@
     method public int describeContents();
     method @NonNull public java.util.List<android.credentials.CredentialOption> getCredentialOptions();
     method @NonNull public android.os.Bundle getData();
+    method @Nullable public String getOrigin();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialRequest> CREATOR;
   }
@@ -13666,6 +13674,7 @@
     method @NonNull public android.credentials.GetCredentialRequest build();
     method @NonNull public android.credentials.GetCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean);
     method @NonNull public android.credentials.GetCredentialRequest.Builder setCredentialOptions(@NonNull java.util.List<android.credentials.CredentialOption>);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public android.credentials.GetCredentialRequest.Builder setOrigin(@NonNull String);
   }
 
   public final class GetCredentialResponse implements android.os.Parcelable {
@@ -19361,7 +19370,7 @@
   public final class OutputConfiguration implements android.os.Parcelable {
     ctor public OutputConfiguration(@NonNull android.view.Surface);
     ctor public OutputConfiguration(int, @NonNull android.view.Surface);
-    ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
+    ctor public <T> OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
     method public void addSensorPixelModeUsed(int);
     method public void addSurface(@NonNull android.view.Surface);
     method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
@@ -40563,7 +40572,9 @@
 
   public final class CallingAppInfo implements android.os.Parcelable {
     ctor public CallingAppInfo(@NonNull String, @NonNull android.content.pm.SigningInfo);
+    ctor public CallingAppInfo(@NonNull String, @NonNull android.content.pm.SigningInfo, @Nullable String);
     method public int describeContents();
+    method @Nullable public String getOrigin();
     method @NonNull public String getPackageName();
     method @NonNull public android.content.pm.SigningInfo getSigningInfo();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -42055,6 +42066,7 @@
   }
 
   public final class CallControl {
+    method public void answer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
     method public void disconnect(@NonNull android.telecom.DisconnectCause, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
     method @NonNull public android.os.ParcelUuid getCallId();
     method public void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8beb5e5..47d9ab6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -843,6 +843,7 @@
     method public int getPendingIntentBackgroundActivityStartMode();
     method public boolean isDeferUntilActive();
     method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed();
+    method @Deprecated @NonNull public static android.app.BroadcastOptions makeBasic();
     method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
     method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index f35bdfb..fe40a4c 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -262,7 +262,12 @@
      * Creates a basic {@link BroadcastOptions} with no options initially set.
      *
      * @return an instance of {@code BroadcastOptions} against which options can be set
+     *
+     * @deprecated Use {@link BroadcastOptions#BroadcastOptions()} instead.
+     * @hide
      */
+    @Deprecated
+    @SystemApi
     public static @NonNull BroadcastOptions makeBasic() {
         BroadcastOptions opts = new BroadcastOptions();
         return opts;
@@ -280,7 +285,10 @@
         return opts;
     }
 
-    private BroadcastOptions() {
+    /**
+     * Creates a new {@code BroadcastOptions} with no options initially set.
+     */
+    public BroadcastOptions() {
         super();
         resetTemporaryAppAllowlist();
     }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index e97e711..d62e15a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -65,12 +65,12 @@
 import android.os.IBinder;
 import android.os.IProgressListener;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
 import android.os.StrictMode;
 import android.os.WorkSource;
 import android.service.voice.IVoiceInteractionSession;
 import android.view.IRecentsAnimationRunner;
 import android.view.IRemoteAnimationRunner;
-import android.view.IWindowFocusObserver;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
 import android.window.IWindowOrganizerController;
@@ -349,12 +349,13 @@
     /**
      * Prepare the back navigation in the server. This setups the leashed for sysui to animate
      * the back gesture and returns the data needed for the animation.
-     * @param focusObserver a remote callback to nofify shell when the focused window lost focus.
+     * @param navigationObserver a remote callback to nofify shell when the focused window is gone,
+                                 or an unexpected transition has happened on the navigation target.
      * @param adaptor a remote animation to be run for the back navigation plays the animation.
      * @return Returns the back navigation info.
      */
     android.window.BackNavigationInfo startBackNavigation(
-            in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor);
+            in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
 
     /**
      * registers a callback to be invoked when the screen is captured.
diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java
index b62f766..bb9a95c 100644
--- a/core/java/android/app/LocaleManager.java
+++ b/core/java/android/app/LocaleManager.java
@@ -198,13 +198,14 @@
      * the override config, and stored in a system file for future access.
      *
      * <p><b>Note:</b> Using this function, applications can update their list of supported
-     * locales while running, without an update of the application’s software. For more
-     * information, see TODO(b/261528306): add link to guide.
+     * locales while running, without an update of the application’s software.
      *
      * <p>Applications can remove the override LocaleConfig with a {@code null} object.
      *
      * @param localeConfig the desired {@link LocaleConfig} for the calling app.
      */
+     // Add following to last Note: when guide is written:
+     // For more information, see TODO(b/261528306): add link to guide.
     @UserHandleAware
     public void setOverrideLocaleConfig(@Nullable LocaleConfig localeConfig) {
         try {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ef5cd93..440ee20 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5758,7 +5758,7 @@
             List<Notification.Action> nonContextualActions = getNonContextualActions();
 
             int numActions = Math.min(nonContextualActions.size(), MAX_ACTION_BUTTONS);
-            boolean emphazisedMode = mN.fullScreenIntent != null
+            boolean emphasizedMode = mN.fullScreenIntent != null
                     || p.mCallStyleActions
                     || ((mN.flags & FLAG_FSI_REQUESTED_BUT_DENIED) != 0);
 
@@ -5771,7 +5771,7 @@
                 big.setInt(R.id.actions, "setCollapsibleIndentDimen",
                         R.dimen.call_notification_collapsible_indent);
             }
-            big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
+            big.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
             if (numActions > 0 && !p.mHideActions) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
@@ -5783,12 +5783,12 @@
                     boolean actionHasValidInput = hasValidRemoteInput(action);
                     validRemoteInput |= actionHasValidInput;
 
-                    final RemoteViews button = generateActionButton(action, emphazisedMode, p);
-                    if (actionHasValidInput && !emphazisedMode) {
+                    final RemoteViews button = generateActionButton(action, emphasizedMode, p);
+                    if (actionHasValidInput && !emphasizedMode) {
                         // Clear the drawable
                         button.setInt(R.id.action0, "setBackgroundResource", 0);
                     }
-                    if (emphazisedMode && i > 0) {
+                    if (emphasizedMode && i > 0) {
                         // Clear start margin from non-first buttons to reduce the gap between them.
                         //  (8dp remaining gap is from all buttons' standard 4dp inset).
                         button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
@@ -7475,10 +7475,10 @@
             Resources resources = context.getResources();
             boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
             if (mPictureIcon != null) {
-                int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
+                int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
                         ? R.dimen.notification_big_picture_max_height_low_ram
                         : R.dimen.notification_big_picture_max_height);
-                int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
+                int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
                         ? R.dimen.notification_big_picture_max_width_low_ram
                         : R.dimen.notification_big_picture_max_width);
                 mPictureIcon.scaleDownIfNecessary(maxPictureWidth, maxPictureHeight);
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 50a7da1..5ee10a5 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -115,6 +115,20 @@
     String SHELL_COMMAND_CONFIRM_TIME = "confirm_time";
 
     /**
+     * A shell command that clears the network time signal used by {@link
+     * SystemClock#currentNetworkTimeClock()}.
+     * @hide
+     */
+    String SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME = "clear_system_clock_network_time";
+
+    /**
+     * A shell command that sets the network time signal used by {@link
+     * SystemClock#currentNetworkTimeClock()}.
+     * @hide
+     */
+    String SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME = "set_system_clock_network_time";
+
+    /**
      * A shared utility method to create a {@link ManualTimeSuggestion}.
      *
      * @hide
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index e981581..2b400c1f 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -155,6 +155,11 @@
     AttributionSource(@NonNull Parcel in) {
         this(AttributionSourceState.CREATOR.createFromParcel(in));
 
+        if (!Binder.isDirectlyHandlingTransaction()) {
+            throw new SecurityException("AttributionSource should be unparceled during a binder "
+                    + "transaction for proper verification.");
+        }
+
         // Since we just unpacked this object as part of it transiting a Binder
         // call, this is the perfect time to enforce that its UID and PID can be trusted
         enforceCallingUidAndPid();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5818ed7..85daf15 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3234,8 +3234,9 @@
     /**
      * Broadcast Action: The receiver's effective locale has changed.
      *
-     * This happens when the device locale, or the receiving app's locale
-     * (set via {@link android.app.LocaleManager#setApplicationLocales}) changed.
+     * This happens when the device locale, the receiving app's locale
+     * (set via {@link android.app.LocaleManager#setApplicationLocales}) or language tags
+     * of Regional preferences changed.
      *
      * Can be received by manifest-declared receivers.
      *
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8acdf51..a6a6215 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1711,8 +1711,8 @@
          * performed on the session. In case of device reboot or data loader transient failure
          * before the session has been finalized, you may commit the session again.
          * <p>
-         * If the installer is the device owner or the affiliated profile owner, there will be no
-         * user intervention.
+         * If the installer is the device owner, the affiliated profile owner, or has received
+         * user pre-approval of this session, there will be no user intervention.
          *
          * @param statusReceiver Called when the state of the session changes. Intents
          *                       sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
@@ -1722,6 +1722,7 @@
          *             {@link #openWrite(String, long, long)} are still open.
          *
          * @see android.app.admin.DevicePolicyManager
+         * @see #requestUserPreapproval
          */
         public void commit(@NonNull IntentSender statusReceiver) {
             try {
@@ -1987,14 +1988,22 @@
          * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES REQUEST_INSTALL_PACKAGES}
          * permission, they can request the approval from users before
          * {@link Session#commit(IntentSender)} is called. This may require user intervention as
-         * well. The result of the request will be reported through the given callback.
+         * well. When user intervention is required, installers will receive a
+         * {@link #STATUS_PENDING_USER_ACTION} callback, and {@link #STATUS_SUCCESS} otherwise.
+         * In case that requesting user pre-approval is not available, installers will receive
+         * {@link #STATUS_FAILURE_BLOCKED} instead. Note that if the users decline the request,
+         * this session will be abandoned.
+         *
+         * If user intervention is required but never resolved, or requesting user
+         * pre-approval is not available, you may still call {@link Session#commit(IntentSender)}
+         * as the typical installation.
          *
          * @param details the adequate context to this session for requesting the approval from
          *                users prior to commit.
          * @param statusReceiver called when the state of the session changes.
-         *                       Intents sent to this receiver contain
-         *                       {@link #EXTRA_STATUS}. Refer to the individual
-         *                       status codes on how to handle them.
+         *                       Intents sent to this receiver contain {@link #EXTRA_STATUS}
+         *                       and the {@link #EXTRA_PRE_APPROVAL} would be {@code true}.
+         *                       Refer to the individual status codes on how to handle them.
          *
          * @throws IllegalArgumentException when {@link PreapprovalDetails} is {@code null}.
          * @throws IllegalArgumentException if {@link IntentSender} is {@code null}.
@@ -2003,6 +2012,7 @@
          * @throws IllegalStateException if called again after this method has been called on
          *                               this session.
          * @throws SecurityException when the caller does not own this session.
+         * @throws SecurityException if called after the session has been committed or abandoned.
          */
         public void requestUserPreapproval(@NonNull PreapprovalDetails details,
                 @NonNull IntentSender statusReceiver) {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index f3ad9d1..02b9308 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -361,7 +361,7 @@
     /**
      * Constant corresponding to {@code systemExempted} in
      * the {@link android.R.attr#foregroundServiceType} attribute.
-     * The system exmpted foreground service use cases.
+     * The system exempted foreground service use cases.
      *
      * <p class="note">Note, apps are allowed to use this type only in the following cases:
      * <ul>
diff --git a/core/java/android/credentials/CreateCredentialRequest.java b/core/java/android/credentials/CreateCredentialRequest.java
index 1e2a86f..b756a43 100644
--- a/core/java/android/credentials/CreateCredentialRequest.java
+++ b/core/java/android/credentials/CreateCredentialRequest.java
@@ -16,9 +16,14 @@
 
 package android.credentials;
 
+import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
+
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -69,6 +74,14 @@
     private final boolean mIsSystemProviderRequired;
 
     /**
+     * The origin of the calling app. Callers of this special API (e.g. browsers)
+     * can set this origin for an app different from their own, to be able to get credentials
+     * on behalf of that app.
+     */
+    @Nullable
+    private final String mOrigin;
+
+    /**
      * Returns the requested credential type.
      */
     @NonNull
@@ -123,6 +136,14 @@
         return mAlwaysSendAppInfoToProvider;
     }
 
+    /**
+     * Returns the origin of the calling app if set otherwise returns null.
+     */
+    @Nullable
+    public String getOrigin() {
+        return mOrigin;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString8(mType);
@@ -130,6 +151,7 @@
         dest.writeBundle(mCandidateQueryData);
         dest.writeBoolean(mIsSystemProviderRequired);
         dest.writeBoolean(mAlwaysSendAppInfoToProvider);
+        dest.writeString8(mOrigin);
     }
 
     @Override
@@ -146,6 +168,7 @@
                 + ", isSystemProviderRequired=" + mIsSystemProviderRequired
                 + ", alwaysSendAppInfoToProvider="
                 + mAlwaysSendAppInfoToProvider
+                + ", origin=" + mOrigin
                 + "}";
     }
 
@@ -165,21 +188,26 @@
      *                                    the query phase, and will only be sent along
      *                                    with the final request, after the user has selected
      *                                    an entry on the UI.
+     * @param origin the origin of the calling app. Callers of this special setter (e.g. browsers)
+     *               can set this origin for an app different from their own, to be able to get
+     *               credentials on behalf of that app.
      *
      * @throws IllegalArgumentException If type is empty.
      */
-    public CreateCredentialRequest(
+    private CreateCredentialRequest(
             @NonNull String type,
             @NonNull Bundle credentialData,
             @NonNull Bundle candidateQueryData,
             boolean isSystemProviderRequired,
-            boolean alwaysSendAppInfoToProvider) {
+            boolean alwaysSendAppInfoToProvider,
+            @NonNull String origin) {
         mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
         mCredentialData = requireNonNull(credentialData, "credentialData must not be null");
         mCandidateQueryData = requireNonNull(candidateQueryData,
                 "candidateQueryData must not be null");
         mIsSystemProviderRequired = isSystemProviderRequired;
         mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider;
+        mOrigin = origin;
     }
 
     private CreateCredentialRequest(@NonNull Parcel in) {
@@ -188,6 +216,7 @@
         Bundle candidateQueryData = in.readBundle();
         boolean isSystemProviderRequired = in.readBoolean();
         boolean alwaysSendAppInfoToProvider = in.readBoolean();
+        mOrigin = in.readString8();
 
         mType = type;
         AnnotationValidations.validate(NonNull.class, null, mType);
@@ -211,4 +240,104 @@
             return new CreateCredentialRequest(in);
         }
     };
+
+    /** A builder for {@link CreateCredentialRequest}. */
+    public static final class Builder {
+
+        private boolean mAlwaysSendAppInfoToProvider;
+
+        @NonNull
+        private String mType;
+
+        @NonNull
+        private final Bundle mCredentialData;
+
+        @NonNull
+        private final Bundle mCandidateQueryData;
+
+        private boolean mIsSystemProviderRequired;
+
+        private String mOrigin;
+
+        /**
+         * @param credentialData the full credential creation request data
+         * @param candidateQueryData the partial request data that will be sent to the provider
+         *                           during the initial creation candidate query stage
+         */
+        public Builder(@NonNull Bundle credentialData, @NonNull Bundle candidateQueryData) {
+            mCredentialData = requireNonNull(credentialData,
+                    "credentialData must not be null");
+            mCandidateQueryData = requireNonNull(candidateQueryData,
+                    "candidateQueryData must not be null");
+        }
+
+        /**
+         * Sets a true/false value to determine if the calling app info should be
+         * removed from the request that is sent to the providers.
+         *
+         * Developers must set this to false if they wish to remove the
+         * {@link android.service.credentials.CallingAppInfo} from the query phases requests that
+         * providers receive. Note that the calling app info will still be sent in the
+         * final phase after the user has made a selection on the UI.
+         *
+         * If not set, the default value will be true and the calling app info will be
+         * propagated to the providers in every phase.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public CreateCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean value) {
+            mAlwaysSendAppInfoToProvider = value;
+            return this;
+        }
+
+        /**
+         * Sets the requested credential type.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public CreateCredentialRequest.Builder setType(@NonNull String type) {
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Sets whether the request must only be fulfilled by a system provider.
+         * This defaults to false
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public CreateCredentialRequest.Builder setIsSystemProviderRequired(boolean value) {
+            mIsSystemProviderRequired = value;
+            return this;
+        }
+
+        /**
+         * Sets the origin of the calling app. Callers of this special setter (e.g. browsers)
+         * can set this origin for an app different from their own, to be able to get
+         * credentials on behalf of that app. The permission check only happens later when this
+         * instance is passed and processed by the Credential Manager.
+         */
+        @SuppressLint({"MissingGetterMatchingBuilder", "AndroidFrameworkRequiresPermission"})
+        @RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
+        @NonNull
+        public CreateCredentialRequest.Builder setOrigin(@NonNull String origin) {
+            mOrigin = origin;
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetCredentialRequest}.
+         *
+         * @throws IllegalArgumentException If credentialOptions is empty.
+         */
+        @NonNull
+        public CreateCredentialRequest build() {
+            Preconditions.checkStringNotEmpty(
+                    mType,
+                    "type must not be empty");
+
+            return new CreateCredentialRequest(mType, mCredentialData, mCandidateQueryData,
+                    mIsSystemProviderRequired, mAlwaysSendAppInfoToProvider, mOrigin);
+        }
+    }
 }
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 87281cc..f0230e7 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -123,6 +123,10 @@
      *
      * <p>The execution can potentially launch UI flows to collect user consent to using a
      * credential, display a picker when multiple credentials exist, etc.
+     * Callers (e.g. browsers) may optionally set origin in {@link GetCredentialRequest} for an
+     * app different from their own, to be able to get credentials on behalf of that app. They would
+     * need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
+     * to use this functionality
      *
      * @param request the request specifying type(s) of credentials to get from the user
      * @param activity the activity used to launch any UI needed
@@ -163,61 +167,14 @@
     }
 
     /**
-     * Launches the necessary flows to retrieve an app credential from the user, for the given
-     * origin.
-     *
-     * <p>The execution can potentially launch UI flows to collect user consent to using a
-     * credential, display a picker when multiple credentials exist, etc.
-     *
-     * @param request the request specifying type(s) of credentials to get from the user
-     * @param origin the origin of the calling app. Callers of this special API (e.g. browsers)
-     * can set this origin for an app different from their own, to be able to get credentials
-     * on behalf of that app.
-     * @param activity the activity used to launch any UI needed
-     * @param cancellationSignal an optional signal that allows for cancelling this call
-     * @param executor the callback will take place on this {@link Executor}
-     * @param callback the callback invoked when the request succeeds or fails
-     */
-    @RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
-    public void getCredentialWithOrigin(
-            @NonNull GetCredentialRequest request,
-            @Nullable String origin,
-            @NonNull Activity activity,
-            @Nullable CancellationSignal cancellationSignal,
-            @CallbackExecutor @NonNull Executor executor,
-            @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
-        requireNonNull(request, "request must not be null");
-        requireNonNull(activity, "activity must not be null");
-        requireNonNull(executor, "executor must not be null");
-        requireNonNull(callback, "callback must not be null");
-
-        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
-            Log.w(TAG, "getCredential already canceled");
-            return;
-        }
-
-        ICancellationSignal cancelRemote = null;
-        try {
-            cancelRemote =
-                mService.executeGetCredentialWithOrigin(
-                    request,
-                    new GetCredentialTransport(activity, executor, callback),
-                    mContext.getOpPackageName(),
-                    origin);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-
-        if (cancellationSignal != null && cancelRemote != null) {
-            cancellationSignal.setRemote(cancelRemote);
-        }
-    }
-
-    /**
      * Launches the necessary flows to register an app credential for the user.
      *
      * <p>The execution can potentially launch UI flows to collect user consent to creating or
      * storing the new credential, etc.
+     * Callers (e.g. browsers) may optionally set origin in {@link CreateCredentialRequest} for an
+     * app different from their own, to be able to get credentials on behalf of that app. They would
+     * need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
+     * to use this functionality
      *
      * @param request the request specifying type(s) of credentials to get from the user
      * @param activity the activity used to launch any UI needed
@@ -259,58 +216,6 @@
     }
 
     /**
-     * Launches the necessary flows to register an app credential for the user.
-     *
-     * <p>The execution can potentially launch UI flows to collect user consent to creating or
-     * storing the new credential, etc.
-     *
-     * @param request the request specifying type(s) of credentials to get from the user, for the
-     * given origin
-     * @param origin the origin of the calling app. Callers of this special API (e.g. browsers)
-     * can set this origin for an app different from their own, to be able to get credentials
-     * on behalf of that app.
-     * @param activity the activity used to launch any UI needed
-     * @param cancellationSignal an optional signal that allows for cancelling this call
-     * @param executor the callback will take place on this {@link Executor}
-     * @param callback the callback invoked when the request succeeds or fails
-     */
-    @RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
-    public void createCredentialWithOrigin(
-            @NonNull CreateCredentialRequest request,
-            @Nullable String origin,
-            @NonNull Activity activity,
-            @Nullable CancellationSignal cancellationSignal,
-            @CallbackExecutor @NonNull Executor executor,
-            @NonNull
-            OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
-        requireNonNull(request, "request must not be null");
-        requireNonNull(activity, "activity must not be null");
-        requireNonNull(executor, "executor must not be null");
-        requireNonNull(callback, "callback must not be null");
-
-        if (cancellationSignal != null && cancellationSignal.isCanceled()) {
-            Log.w(TAG, "createCredential already canceled");
-            return;
-        }
-
-        ICancellationSignal cancelRemote = null;
-        try {
-            cancelRemote =
-                mService.executeCreateCredentialWithOrigin(
-                    request,
-                    new CreateCredentialTransport(activity, executor, callback),
-                    mContext.getOpPackageName(),
-                    origin);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-
-        if (cancellationSignal != null && cancelRemote != null) {
-            cancellationSignal.setRemote(cancelRemote);
-        }
-    }
-
-    /**
      * Clears the current user credential state from all credential providers.
      *
      * <p>You should invoked this api after your user signs out of your app to notify all credential
diff --git a/core/java/android/credentials/GetCredentialRequest.java b/core/java/android/credentials/GetCredentialRequest.java
index bc92c7c..951cbe4 100644
--- a/core/java/android/credentials/GetCredentialRequest.java
+++ b/core/java/android/credentials/GetCredentialRequest.java
@@ -16,9 +16,13 @@
 
 package android.credentials;
 
+import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
+
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -49,6 +53,14 @@
     private final Bundle mData;
 
     /**
+     * The origin of the calling app. Callers of this special API (e.g. browsers)
+     * can set this origin for an app different from their own, to be able to get credentials
+     * on behalf of that app.
+     */
+    @Nullable
+    private String mOrigin;
+
+    /**
      * True/False value to determine if the calling app info should be
      * removed from the request that is sent to the providers.
      * Developers must set this to false if they wish to remove the
@@ -76,6 +88,14 @@
     }
 
     /**
+     * Returns the origin of the calling app if set otherwise returns null.
+     */
+    @Nullable
+    public String getOrigin() {
+        return mOrigin;
+    }
+
+    /**
      * Returns a value to determine if the calling app info should be always
      * sent to the provider in every phase (if true), or should be removed
      * from the query phase, and only sent as part of the request in the final phase,
@@ -90,6 +110,7 @@
         dest.writeTypedList(mCredentialOptions, flags);
         dest.writeBundle(mData);
         dest.writeBoolean(mAlwaysSendAppInfoToProvider);
+        dest.writeString8(mOrigin);
     }
 
     @Override
@@ -103,11 +124,12 @@
                 + ", data=" + mData
                 + ", alwaysSendAppInfoToProvider="
                 + mAlwaysSendAppInfoToProvider
+                + ", origin=" + mOrigin
                 + "}";
     }
 
     private GetCredentialRequest(@NonNull List<CredentialOption> credentialOptions,
-            @NonNull Bundle data, @NonNull boolean alwaysSendAppInfoToProvider) {
+            @NonNull Bundle data, @NonNull boolean alwaysSendAppInfoToProvider, String origin) {
         Preconditions.checkCollectionNotEmpty(
                 credentialOptions,
                 /*valueName=*/ "credentialOptions");
@@ -118,6 +140,7 @@
         mData = requireNonNull(data,
                 "data must not be null");
         mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider;
+        mOrigin = origin;
     }
 
     private GetCredentialRequest(@NonNull Parcel in) {
@@ -132,6 +155,7 @@
         AnnotationValidations.validate(NonNull.class, null, mData);
 
         mAlwaysSendAppInfoToProvider = in.readBoolean();
+        mOrigin = in.readString8();
     }
 
     @NonNull public static final Parcelable.Creator<GetCredentialRequest> CREATOR =
@@ -159,6 +183,8 @@
         @NonNull
         private boolean mAlwaysSendAppInfoToProvider = true;
 
+        private String mOrigin;
+
         /**
          * @param data the top request level data
          */
@@ -209,6 +235,20 @@
         }
 
         /**
+         * Sets the origin of the calling app. Callers of this special setter (e.g. browsers)
+         * can set this origin for an app different from their own, to be able to get
+         * credentials on behalf of that app. The permission check only happens later when this
+         * instance is passed and processed by the Credential Manager.
+         */
+        @SuppressLint({"MissingGetterMatchingBuilder", "AndroidFrameworkRequiresPermission"})
+        @RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
+        @NonNull
+        public Builder setOrigin(@NonNull String origin) {
+            mOrigin = origin;
+            return this;
+        }
+
+        /**
          * Builds a {@link GetCredentialRequest}.
          *
          * @throws IllegalArgumentException If credentialOptions is empty.
@@ -222,7 +262,7 @@
                     mCredentialOptions,
                     /*valueName=*/ "credentialOptions");
             return new GetCredentialRequest(mCredentialOptions, mData,
-                    mAlwaysSendAppInfoToProvider);
+                    mAlwaysSendAppInfoToProvider, mOrigin);
         }
     }
 }
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 6d81d80..625fc8a 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -41,12 +41,8 @@
 
     @nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage);
 
-    @nullable ICancellationSignal executeGetCredentialWithOrigin(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage, String origin);
-
     @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
 
-    @nullable ICancellationSignal executeCreateCredentialWithOrigin(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage, String origin);
-
     @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
 
     @nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);
diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java
index 67634dc..3c10e81 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -78,7 +78,7 @@
                         Resources.getSystem()
                                 .getString(
                                         com.android.internal.R.string
-                                                .config_credentialManagerDialogComponent));
+                                                .config_credentialManagerReceiverComponent));
         intent.setComponent(componentName);
         intent.setAction(Constants.CREDMAN_ENABLED_PROVIDERS_UPDATED);
         return intent;
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 19719a8..fbc0184 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -142,24 +142,18 @@
                     PackageManager.PERMISSION_GRANTED;
         }
 
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
         mFoldStateListener = new FoldStateListener(context);
         try {
-            context.getSystemService(DeviceStateManager.class)
-                    .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
+            context.getSystemService(DeviceStateManager.class).registerCallback(
+                    new HandlerExecutor(CameraManagerGlobal.get().getDeviceStateHandler()),
+                    mFoldStateListener);
         } catch (IllegalStateException e) {
             Log.v(TAG, "Failed to register device state listener!");
             Log.v(TAG, "Device state dependent characteristics updates will not be functional!");
-            mHandlerThread.quitSafely();
-            mHandler = null;
             mFoldStateListener = null;
         }
     }
 
-    private HandlerThread mHandlerThread;
-    private Handler mHandler;
     private FoldStateListener mFoldStateListener;
 
     /**
@@ -1645,6 +1639,9 @@
         private ICameraService mCameraService;
         private boolean mHasOpenCloseListenerPermission = false;
 
+        private HandlerThread mDeviceStateHandlerThread;
+        private Handler mDeviceStateHandler;
+
         // Singleton, don't allow construction
         private CameraManagerGlobal() { }
 
@@ -1658,6 +1655,18 @@
             return gCameraManager;
         }
 
+        public Handler getDeviceStateHandler() {
+            synchronized(mLock) {
+                if (mDeviceStateHandlerThread == null) {
+                    mDeviceStateHandlerThread = new HandlerThread(TAG);
+                    mDeviceStateHandlerThread.start();
+                    mDeviceStateHandler = new Handler(mDeviceStateHandlerThread.getLooper());
+                }
+
+                return mDeviceStateHandler;
+            }
+        }
+
         @Override
         public IBinder asBinder() {
             return this;
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 4009fa7..1c6de04 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -52,7 +52,7 @@
     }
 
     /** The sensor id for this operation. */
-    private final int mSensorId;
+    private int mSensorId;
     private static int defaultSensorId() {
         return -1;
     }
@@ -299,6 +299,15 @@
     }
 
     /**
+     * The sensor id for this operation.
+     */
+    @DataClass.Generated.Member
+    public @NonNull FaceAuthenticateOptions setSensorId( int value) {
+        mSensorId = value;
+        return this;
+    }
+
+    /**
      * The package name for that operation that should be used for
      * {@link android.app.AppOpsManager} verification.
      *
@@ -610,10 +619,10 @@
     }
 
     @DataClass.Generated(
-            time = 1676508211385L,
+            time = 1677119626034L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java",
-            inputSignatures = "private final  int mUserId\nprivate final  int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final  int AUTHENTICATE_REASON_UNKNOWN\npublic static final  int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final  int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final  int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final  int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final  int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final  int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final  int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final  int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  int defaultDisplayState()\nprivate static  int defaultAuthenticateReason()\nprivate static  int defaultWakeReason()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final  int AUTHENTICATE_REASON_UNKNOWN\npublic static final  int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final  int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final  int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final  int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final  int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final  int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final  int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final  int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  int defaultDisplayState()\nprivate static  int defaultAuthenticateReason()\nprivate static  int defaultWakeReason()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 2857627..9d5073e 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -68,7 +68,7 @@
     // by BiometricService. To start authentication after the clients are ready, use
     // startPreparedClient().
     @EnforcePermission("USE_BIOMETRIC_INTERNAL")
-    void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token,
+    void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long operationId, IBiometricSensorReceiver sensorReceiver,
             in FaceAuthenticateOptions options, long requestId, int cookie,
             boolean allowBackgroundAuthentication);
diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
index cecb317..763246e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
+++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
@@ -46,7 +46,7 @@
     }
 
     /** The sensor id for this operation. */
-    private final int mSensorId;
+    private int mSensorId;
     private static int defaultSensorId() {
         return SENSOR_ID_ANY;
     }
@@ -176,6 +176,15 @@
     }
 
     /**
+     * The sensor id for this operation.
+     */
+    @DataClass.Generated.Member
+    public @NonNull FingerprintAuthenticateOptions setSensorId( int value) {
+        mSensorId = value;
+        return this;
+    }
+
+    /**
      * The package name for that operation that should be used for
      * {@link android.app.AppOpsManager} verification.
      *
@@ -433,10 +442,10 @@
     }
 
     @DataClass.Generated(
-            time = 1676508212083L,
+            time = 1677119626721L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java",
-            inputSignatures = "private final  int mUserId\nprivate final  int mSensorId\nprivate final  boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  boolean defaultIgnoreEnrollmentState()\nprivate static  int defaultDisplayState()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final  boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  boolean defaultIgnoreEnrollmentState()\nprivate static  int defaultDisplayState()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index e3ae299..ec5749e 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -73,8 +73,8 @@
     // by BiometricService. To start authentication after the clients are ready, use
     // startPreparedClient().
     @EnforcePermission("MANAGE_BIOMETRIC")
-    void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId,
-            IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
+    void prepareForAuthentication(IBinder token, long operationId,
+            IBiometricSensorReceiver sensorReceiver, in FingerprintAuthenticateOptions options, long requestId,
             int cookie, boolean allowBackgroundAuthentication);
 
     // Starts authentication with the previously prepared client.
diff --git a/core/java/android/os/CancellationSignalBeamer.java b/core/java/android/os/CancellationSignalBeamer.java
new file mode 100644
index 0000000..afb5ff7
--- /dev/null
+++ b/core/java/android/os/CancellationSignalBeamer.java
@@ -0,0 +1,325 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.system.SystemCleaner;
+
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+import java.util.HashMap;
+
+/**
+ * A transport for {@link CancellationSignal}, but unlike
+ * {@link CancellationSignal#createTransport()} doesn't require pre-creating the transport in the
+ * target process. Instead, cancellation is forwarded over the same IPC surface as the cancellable
+ * request.
+ *
+ * <p><strong>Important:</strong> For this to work, the following invariants must be held up:
+ * <ul>
+ *     <li>A call to beam() <strong>MUST</strong> result in a call to close() on the result
+ *     (otherwise, the token will be leaked and cancellation isn't propagated), and that call
+ *     must happen after the call using the
+ *     token is sent (otherwise, any concurrent cancellation may be lost). It is strongly
+ *     recommended to use try-with-resources on the token.
+ *     <li>The cancel(), forget() and cancellable operations transporting the token must either
+ *     all be oneway on the same binder, or all be non-oneway to guarantee proper ordering.
+ *     <li>A {@link CancellationSignal} <strong>SHOULD</strong> be used only once, as there
+ *     can only be a single {@link android.os.CancellationSignal.OnCancelListener OnCancelListener}.
+ *
+ * </ul>
+ * <p>Caveats:
+ * <ul>
+ *     <li>Cancellation is only ever dispatched after the token is closed, and thus after the
+ *     call performing the cancellable operation (if the invariants are followed). The operation
+ *     must therefore not block the incoming binder thread, or cancellation won't be possible.
+ *     <li>Consequently, in the unlikely event that the sender dies right after beaming an already
+ *     cancelled {@link CancellationSignal}, the cancellation may be lost (unlike with
+ *     {@link CancellationSignal#createTransport()}).
+ *     <li>The forwarding OnCancelListener is set in the implied finally phase of try-with-resources
+ *         / when closing the token. If the receiver is in the same process, and the signal is
+ *         already cancelled, this may invoke the target's OnCancelListener during that phase.
+ * </ul>
+ *
+ *
+ * <p>Usage:
+ * <pre>
+ *  // Sender:
+ *
+ *  class FooManager {
+ *    var mCancellationSignalSender = new CancellationSignalBeamer.Sender() {
+ *      &#064;Override
+ *      public void onCancel(IBinder token) { remoteIFooService.onCancelToken(token); }
+ *
+ *      &#064;Override
+ *      public void onForget(IBinder token) { remoteIFooService.onForgetToken(token); }
+ *    };
+ *
+ *    public void doCancellableOperation(..., CancellationSignal cs) {
+ *      try (var csToken = mCancellationSignalSender.beam(cs)) {
+ *          remoteIFooService.doCancellableOperation(..., csToken);
+ *      }
+ *    }
+ *  }
+ *
+ *  // Receiver:
+ *
+ *  class FooManagerService extends IFooService.Stub {
+ *    var mCancellationSignalReceiver = new CancellationSignalBeamer.Receiver();
+ *
+ *    &#064;Override
+ *    public void doCancellableOperation(..., IBinder csToken) {
+ *      CancellationSignal cs = mCancellationSignalReceiver.unbeam(csToken))
+ *      // ...
+ *    }
+ *
+ *    &#064;Override
+ *    public void onCancelToken(..., IBinder csToken) {
+ *      mCancellationSignalReceiver.cancelToken(csToken))
+ *    }
+ *
+ *    &#064;Override
+ *    public void onForgetToken(..., IBinder csToken) {
+ *      mCancellationSignalReceiver.forgetToken(csToken))
+ *    }
+ *  }
+ *
+ * </pre>
+ *
+ * @hide
+ */
+public class CancellationSignalBeamer {
+
+    static final Cleaner sCleaner = SystemCleaner.cleaner();
+
+    /** The sending side of an {@link CancellationSignalBeamer} */
+    public abstract static class Sender {
+
+        /**
+         * Beams a {@link CancellationSignal} through an existing Binder interface.
+         *
+         * @param cs the {@code CancellationSignal} to beam, or {@code null}.
+         * @return an {@link IBinder} token. MUST be {@link CloseableToken#close}d <em>after</em>
+         *         the binder call transporting it to the remote process, best with
+         *         try-with-resources. {@code null} if {@code cs} was {@code null}.
+         */
+        // TODO(b/254888024): @MustBeClosed
+        @Nullable
+        public CloseableToken beam(@Nullable CancellationSignal cs) {
+            if (cs == null) {
+                return null;
+            }
+            return new Token(this, cs);
+        }
+
+        /**
+         * A {@link #beam}ed {@link CancellationSignal} was closed.
+         *
+         * MUST be forwarded to {@link Receiver#cancel} with proper ordering. See
+         * {@link CancellationSignalBeamer} for details.
+         */
+        public abstract void onCancel(IBinder token);
+
+        /**
+         * A {@link #beam}ed {@link CancellationSignal} was GC'd.
+         *
+         * MUST be forwarded to {@link Receiver#forget} with proper ordering. See
+         * {@link CancellationSignalBeamer} for details.
+         */
+        public abstract void onForget(IBinder token);
+
+        private static class Token extends Binder implements CloseableToken, Runnable {
+
+            private final Sender mSender;
+            private Preparer mPreparer;
+
+            private Token(Sender sender, CancellationSignal signal) {
+                mSender = sender;
+                mPreparer = new Preparer(sender, signal, this);
+            }
+
+            @Override
+            public void close() {
+                Preparer preparer = mPreparer;
+                mPreparer = null;
+                if (preparer != null) {
+                    preparer.setup();
+                }
+            }
+
+            @Override
+            public void run() {
+                mSender.onForget(this);
+            }
+
+            private static class Preparer implements CancellationSignal.OnCancelListener {
+                private final Sender mSender;
+                private final CancellationSignal mSignal;
+                private final Token mToken;
+
+                private Preparer(Sender sender, CancellationSignal signal, Token token) {
+                    mSender = sender;
+                    mSignal = signal;
+                    mToken = token;
+                }
+
+                void setup() {
+                    sCleaner.register(this, mToken);
+                    mSignal.setOnCancelListener(this);
+                }
+
+                @Override
+                public void onCancel() {
+                    try {
+                        mSender.onCancel(mToken);
+                    } finally {
+                        // Make sure we dispatch onCancel before the cleaner can run.
+                        Reference.reachabilityFence(this);
+                    }
+                }
+            }
+        }
+
+        /**
+         * A {@link #beam}ed {@link CancellationSignal} ready for sending over Binder.
+         *
+         * MUST be closed <em>after</em> it is sent over binder, ideally through try-with-resources.
+         */
+        public interface CloseableToken extends IBinder, AutoCloseable {
+            @Override
+            void close(); // No throws
+        }
+    }
+
+    /** The receiving side of a {@link CancellationSignalBeamer}. */
+    public static class Receiver implements IBinder.DeathRecipient {
+        private final HashMap<IBinder, CancellationSignal> mTokenMap = new HashMap<>();
+        private final boolean mCancelOnSenderDeath;
+
+        /**
+         * Constructs a new {@code Receiver}.
+         *
+         * @param cancelOnSenderDeath if true, {@link CancellationSignal}s obtained from
+         *   {@link #unbeam} are automatically {@link #cancel}led if the sender token
+         *   {@link Binder#linkToDeath dies}; otherwise they are simnply dropped. Note: if the
+         *   sending process drops all references to the {@link CancellationSignal} before
+         *   process death, the cancellation is not guaranteed.
+         */
+        public Receiver(boolean cancelOnSenderDeath) {
+            mCancelOnSenderDeath = cancelOnSenderDeath;
+        }
+
+        /**
+         * Unbeams a token that was obtained via {@link Sender#beam} and turns it back into a
+         * {@link CancellationSignal}.
+         *
+         * A subsequent call to {@link #cancel} with the same token will cancel the returned
+         * {@code CancellationSignal}.
+         *
+         * @param token a token that was obtained from {@link Sender}, possibly in a remote process.
+         * @return a {@link CancellationSignal} linked to the given token.
+         */
+        @Nullable
+        public CancellationSignal unbeam(@Nullable IBinder token) {
+            if (token == null) {
+                return null;
+            }
+            synchronized (this) {
+                CancellationSignal cs = mTokenMap.get(token);
+                if (cs != null) {
+                    return cs;
+                }
+
+                cs = new CancellationSignal();
+                mTokenMap.put(token, cs);
+                try {
+                    token.linkToDeath(this, 0);
+                } catch (RemoteException e) {
+                    dead(token);
+                }
+                return cs;
+            }
+        }
+
+        /**
+         * Forgets state associated with the given token (if any).
+         *
+         * Subsequent calls to {@link #cancel} or binder death notifications on the token will not
+         * have any effect.
+         *
+         * This MUST be invoked when forwarding {@link Sender#onForget}, otherwise the token and
+         * {@link CancellationSignal} will leak if the token was ever {@link #unbeam}ed.
+         *
+         * Optionally, the receiving service logic may also invoke this if it can guarantee that
+         * the unbeamed CancellationSignal isn't needed anymore (i.e. the cancellable operation
+         * using the CancellationSignal has been fully completed).
+         *
+         * @param token the token to forget. No-op if {@code null}.
+         */
+        public void forget(@Nullable IBinder token) {
+            synchronized (this) {
+                if (mTokenMap.remove(token) != null) {
+                    token.unlinkToDeath(this, 0);
+                }
+            }
+        }
+
+        /**
+         * Cancels the {@link CancellationSignal} associated with the given token (if any).
+         *
+         * This MUST be invoked when forwarding {@link Sender#onCancel}, otherwise the token and
+         * {@link CancellationSignal} will leak if the token was ever {@link #unbeam}ed.
+         *
+         * Optionally, the receiving service logic may also invoke this if it can guarantee that
+         * the unbeamed CancellationSignal isn't needed anymore (i.e. the cancellable operation
+         * using the CancellationSignal has been fully completed).
+         *
+         * @param token the token to forget. No-op if {@code null}.
+         */
+        public void cancel(@Nullable IBinder token) {
+            CancellationSignal cs;
+            synchronized (this) {
+                cs = mTokenMap.get(token);
+                if (cs != null) {
+                    forget(token);
+                } else {
+                    return;
+                }
+            }
+            cs.cancel();
+        }
+
+        private void dead(@NonNull IBinder token) {
+            if (mCancelOnSenderDeath) {
+                cancel(token);
+            } else {
+                forget(token);
+            }
+        }
+
+        @Override
+        public void binderDied(@NonNull IBinder who) {
+            dead(who);
+        }
+
+        @Override
+        public void binderDied() {
+            throw new RuntimeException("unreachable");
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index acb1f5b..54e4909 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -116,7 +116,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 
 /**
  * The Settings provider contains global system-level device preferences.
@@ -2979,22 +2978,19 @@
     }
 
     private static final class GenerationTracker {
-        @NonNull private final String mName;
-        @NonNull private final MemoryIntArray mArray;
-        @NonNull private final Consumer<String> mErrorHandler;
+        private final MemoryIntArray mArray;
+        private final Runnable mErrorHandler;
         private final int mIndex;
         private int mCurrentGeneration;
 
-        GenerationTracker(@NonNull String name, @NonNull MemoryIntArray array, int index,
-                int generation, Consumer<String> errorHandler) {
-            mName = name;
+        public GenerationTracker(@NonNull MemoryIntArray array, int index,
+                int generation, Runnable errorHandler) {
             mArray = array;
             mIndex = index;
             mErrorHandler = errorHandler;
             mCurrentGeneration = generation;
         }
 
-        // This method also updates the obsolete generation code stored locally
         public boolean isGenerationChanged() {
             final int currentGeneration = readCurrentGeneration();
             if (currentGeneration >= 0) {
@@ -3015,7 +3011,9 @@
                 return mArray.get(mIndex);
             } catch (IOException e) {
                 Log.e(TAG, "Error getting current generation", e);
-                mErrorHandler.accept(mName);
+                if (mErrorHandler != null) {
+                    mErrorHandler.run();
+                }
             }
             return -1;
         }
@@ -3025,6 +3023,9 @@
                 mArray.close();
             } catch (IOException e) {
                 Log.e(TAG, "Error closing backing array", e);
+                if (mErrorHandler != null) {
+                    mErrorHandler.run();
+                }
             }
         }
     }
@@ -3087,21 +3088,8 @@
         private final ArraySet<String> mAllFields;
         private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk;
 
-        // Mapping from the name of a setting (or the prefix of a namespace) to a generation tracker
         @GuardedBy("this")
-        private ArrayMap<String, GenerationTracker> mGenerationTrackers = new ArrayMap<>();
-
-        private Consumer<String> mGenerationTrackerErrorHandler = (String name) -> {
-            synchronized (NameValueCache.this) {
-                Log.e(TAG, "Error accessing generation tracker - removing");
-                final GenerationTracker tracker = mGenerationTrackers.get(name);
-                if (tracker != null) {
-                    tracker.destroy();
-                    mGenerationTrackers.remove(name);
-                }
-                mValues.remove(name);
-            }
-        };
+        private GenerationTracker mGenerationTracker;
 
         <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
                 String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
@@ -3190,43 +3178,6 @@
 
         @UnsupportedAppUsage
         public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
-            final boolean isSelf = (userHandle == UserHandle.myUserId());
-            int currentGeneration = -1;
-            boolean needsGenerationTracker = false;
-
-            if (isSelf) {
-                synchronized (NameValueCache.this) {
-                    final GenerationTracker generationTracker = mGenerationTrackers.get(name);
-                    if (generationTracker != null) {
-                        if (generationTracker.isGenerationChanged()) {
-                            if (DEBUG) {
-                                Log.i(TAG, "Generation changed for setting:" + name
-                                        + " type:" + mUri.getPath()
-                                        + " in package:" + cr.getPackageName()
-                                        + " and user:" + userHandle);
-                            }
-                            mValues.remove(name);
-                        } else if (mValues.containsKey(name)) {
-                            if (DEBUG) {
-                                Log.i(TAG, "Cache hit for setting:" + name);
-                            }
-                            return mValues.get(name);
-                        }
-                        currentGeneration = generationTracker.getCurrentGeneration();
-                    } else {
-                        needsGenerationTracker = true;
-                    }
-                }
-            } else {
-                if (LOCAL_LOGV) {
-                    Log.v(TAG, "get setting for user " + userHandle
-                            + " by user " + UserHandle.myUserId() + " so skipping cache");
-                }
-            }
-            if (DEBUG) {
-                Log.i(TAG, "Cache miss for setting:" + name);
-            }
-
             // Check if the target settings key is readable. Reject if the caller is not system and
             // is trying to access a settings key defined in the Settings.Secure, Settings.System or
             // Settings.Global and is not annotated as @Readable.
@@ -3260,6 +3211,31 @@
                 }
             }
 
+            final boolean isSelf = (userHandle == UserHandle.myUserId());
+            int currentGeneration = -1;
+            if (isSelf) {
+                synchronized (NameValueCache.this) {
+                    if (mGenerationTracker != null) {
+                        if (mGenerationTracker.isGenerationChanged()) {
+                            if (DEBUG) {
+                                Log.i(TAG, "Generation changed for type:"
+                                        + mUri.getPath() + " in package:"
+                                        + cr.getPackageName() +" and user:" + userHandle);
+                            }
+                            mValues.clear();
+                        } else if (mValues.containsKey(name)) {
+                            return mValues.get(name);
+                        }
+                        if (mGenerationTracker != null) {
+                            currentGeneration = mGenerationTracker.getCurrentGeneration();
+                        }
+                    }
+                }
+            } else {
+                if (LOCAL_LOGV) Log.v(TAG, "get setting for user " + userHandle
+                        + " by user " + UserHandle.myUserId() + " so skipping cache");
+            }
+
             IContentProvider cp = mProviderHolder.getProvider(cr);
 
             // Try the fast path first, not using query().  If this
@@ -3268,17 +3244,24 @@
             // interface.
             if (mCallGetCommand != null) {
                 try {
-                    Bundle args = new Bundle();
+                    Bundle args = null;
                     if (!isSelf) {
+                        args = new Bundle();
                         args.putInt(CALL_METHOD_USER_KEY, userHandle);
                     }
-                    if (needsGenerationTracker) {
-                        args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
-                        if (DEBUG) {
-                            Log.i(TAG, "Requested generation tracker for setting:" + name
-                                    + " type:" + mUri.getPath()
-                                    + " in package:" + cr.getPackageName()
-                                    + " and user:" + userHandle);
+                    boolean needsGenerationTracker = false;
+                    synchronized (NameValueCache.this) {
+                        if (isSelf && mGenerationTracker == null) {
+                            needsGenerationTracker = true;
+                            if (args == null) {
+                                args = new Bundle();
+                            }
+                            args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
+                            if (DEBUG) {
+                                Log.i(TAG, "Requested generation tracker for type: "+ mUri.getPath()
+                                        + " in package:" + cr.getPackageName() +" and user:"
+                                        + userHandle);
+                            }
                         }
                     }
                     Bundle b;
@@ -3315,24 +3298,33 @@
                                         final int generation = b.getInt(
                                                 CALL_METHOD_GENERATION_KEY, 0);
                                         if (DEBUG) {
-                                            Log.i(TAG, "Received generation tracker for setting:"
-                                                    + name
-                                                    + " type:" + mUri.getPath()
-                                                    + " in package:" + cr.getPackageName()
-                                                    + " and user:" + userHandle
-                                                    + " with index:" + index);
+                                            Log.i(TAG, "Received generation tracker for type:"
+                                                    + mUri.getPath() + " in package:"
+                                                    + cr.getPackageName() + " and user:"
+                                                    + userHandle + " with index:" + index);
                                         }
-                                        mGenerationTrackers.put(name, new GenerationTracker(name,
-                                                array, index, generation,
-                                                mGenerationTrackerErrorHandler));
+                                        if (mGenerationTracker != null) {
+                                            mGenerationTracker.destroy();
+                                        }
+                                        mGenerationTracker = new GenerationTracker(array, index,
+                                                generation, () -> {
+                                            synchronized (NameValueCache.this) {
+                                                Log.e(TAG, "Error accessing generation"
+                                                        + " tracker - removing");
+                                                if (mGenerationTracker != null) {
+                                                    GenerationTracker generationTracker =
+                                                            mGenerationTracker;
+                                                    mGenerationTracker = null;
+                                                    generationTracker.destroy();
+                                                    mValues.clear();
+                                                }
+                                            }
+                                        });
                                         currentGeneration = generation;
                                     }
                                 }
-                                if (mGenerationTrackers.get(name) != null && currentGeneration
-                                        == mGenerationTrackers.get(name).getCurrentGeneration()) {
-                                    if (DEBUG) {
-                                        Log.i(TAG, "Updating cache for setting:" + name);
-                                    }
+                                if (mGenerationTracker != null && currentGeneration ==
+                                        mGenerationTracker.getCurrentGeneration()) {
                                     mValues.put(name, value);
                                 }
                             }
@@ -3375,14 +3367,15 @@
 
                 String value = c.moveToNext() ? c.getString(0) : null;
                 synchronized (NameValueCache.this) {
-                    if (mGenerationTrackers.get(name) != null && currentGeneration
-                            == mGenerationTrackers.get(name).getCurrentGeneration()) {
-                        if (DEBUG) {
-                            Log.i(TAG, "Updating cache for setting:" + name + " using query");
-                        }
+                    if (mGenerationTracker != null
+                            && currentGeneration == mGenerationTracker.getCurrentGeneration()) {
                         mValues.put(name, value);
                     }
                 }
+                if (LOCAL_LOGV) {
+                    Log.v(TAG, "cache miss [" + mUri.getLastPathSegment() + "]: " +
+                            name + " = " + (value == null ? "(null)" : value));
+                }
                 return value;
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
@@ -3416,29 +3409,18 @@
             Config.enforceReadPermission(namespace);
             ArrayMap<String, String> keyValues = new ArrayMap<>();
             int currentGeneration = -1;
-            boolean needsGenerationTracker = false;
 
             synchronized (NameValueCache.this) {
-                final GenerationTracker generationTracker = mGenerationTrackers.get(prefix);
-                if (generationTracker != null) {
-                    if (generationTracker.isGenerationChanged()) {
+                if (mGenerationTracker != null) {
+                    if (mGenerationTracker.isGenerationChanged()) {
                         if (DEBUG) {
-                            Log.i(TAG, "Generation changed for prefix:" + prefix
-                                    + " type:" + mUri.getPath()
+                            Log.i(TAG, "Generation changed for type:" + mUri.getPath()
                                     + " in package:" + cr.getPackageName());
                         }
-                        for (int i = 0; i < mValues.size(); ++i) {
-                            String key = mValues.keyAt(i);
-                            if (key.startsWith(prefix)) {
-                                mValues.remove(key);
-                            }
-                        }
+                        mValues.clear();
                     } else {
                         boolean prefixCached = mValues.containsKey(prefix);
                         if (prefixCached) {
-                            if (DEBUG) {
-                                Log.i(TAG, "Cache hit for prefix:" + prefix);
-                            }
                             if (!names.isEmpty()) {
                                 for (String name : names) {
                                     if (mValues.containsKey(name)) {
@@ -3458,9 +3440,9 @@
                             return keyValues;
                         }
                     }
-                    currentGeneration = generationTracker.getCurrentGeneration();
-                } else {
-                    needsGenerationTracker = true;
+                    if (mGenerationTracker != null) {
+                        currentGeneration = mGenerationTracker.getCurrentGeneration();
+                    }
                 }
             }
 
@@ -3468,20 +3450,20 @@
                 // No list command specified, return empty map
                 return keyValues;
             }
-            if (DEBUG) {
-                Log.i(TAG, "Cache miss for prefix:" + prefix);
-            }
             IContentProvider cp = mProviderHolder.getProvider(cr);
 
             try {
                 Bundle args = new Bundle();
                 args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
-                if (needsGenerationTracker) {
-                    args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
-                    if (DEBUG) {
-                        Log.i(TAG, "Requested generation tracker for prefix:" + prefix
-                                + " type: " + mUri.getPath()
-                                + " in package:" + cr.getPackageName());
+                boolean needsGenerationTracker = false;
+                synchronized (NameValueCache.this) {
+                    if (mGenerationTracker == null) {
+                        needsGenerationTracker = true;
+                        args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
+                        if (DEBUG) {
+                            Log.i(TAG, "Requested generation tracker for type: "
+                                    + mUri.getPath() + " in package:" + cr.getPackageName());
+                        }
                     }
                 }
 
@@ -3534,22 +3516,32 @@
                             final int generation = b.getInt(
                                     CALL_METHOD_GENERATION_KEY, 0);
                             if (DEBUG) {
-                                Log.i(TAG, "Received generation tracker for prefix:" + prefix
-                                        + " type:" + mUri.getPath()
-                                        + " in package:" + cr.getPackageName()
-                                        + " with index:" + index);
+                                Log.i(TAG, "Received generation tracker for type:"
+                                        + mUri.getPath() + " in package:"
+                                        + cr.getPackageName() + " with index:" + index);
                             }
-                            mGenerationTrackers.put(prefix,
-                                    new GenerationTracker(prefix, array, index, generation,
-                                            mGenerationTrackerErrorHandler));
+                            if (mGenerationTracker != null) {
+                                mGenerationTracker.destroy();
+                            }
+                            mGenerationTracker = new GenerationTracker(array, index,
+                                    generation, () -> {
+                                synchronized (NameValueCache.this) {
+                                    Log.e(TAG, "Error accessing generation tracker"
+                                            + " - removing");
+                                    if (mGenerationTracker != null) {
+                                        GenerationTracker generationTracker =
+                                                mGenerationTracker;
+                                        mGenerationTracker = null;
+                                        generationTracker.destroy();
+                                        mValues.clear();
+                                    }
+                                }
+                            });
                             currentGeneration = generation;
                         }
                     }
-                    if (mGenerationTrackers.get(prefix) != null && currentGeneration
-                            == mGenerationTrackers.get(prefix).getCurrentGeneration()) {
-                        if (DEBUG) {
-                            Log.i(TAG, "Updating cache for prefix:" + prefix);
-                        }
+                    if (mGenerationTracker != null && currentGeneration
+                            == mGenerationTracker.getCurrentGeneration()) {
                         // cache the complete list of flags for the namespace
                         mValues.putAll(flagsToValues);
                         // Adding the prefix as a signal that the prefix is cached.
@@ -3565,11 +3557,11 @@
 
         public void clearGenerationTrackerForTest() {
             synchronized (NameValueCache.this) {
-                for (int i = 0; i < mGenerationTrackers.size(); i++) {
-                    mGenerationTrackers.valueAt(i).destroy();
+                if (mGenerationTracker != null) {
+                    mGenerationTracker.destroy();
                 }
-                mGenerationTrackers.clear();
                 mValues.clear();
+                mGenerationTracker = null;
             }
         }
     }
diff --git a/core/java/android/service/credentials/BeginGetCredentialOption.java b/core/java/android/service/credentials/BeginGetCredentialOption.java
index 81b9f22..1ad0424 100644
--- a/core/java/android/service/credentials/BeginGetCredentialOption.java
+++ b/core/java/android/service/credentials/BeginGetCredentialOption.java
@@ -116,12 +116,12 @@
      * @param id the unique id associated with this option
      * @param type               the requested credential type
      * @param candidateQueryData the request candidateQueryData
-     * @throws IllegalArgumentException If type is empty.
+     * @throws IllegalArgumentException If id or type is empty.
      */
     public BeginGetCredentialOption(
             @NonNull String id, @NonNull String type,
             @NonNull Bundle candidateQueryData) {
-        mId = id;
+        mId = Preconditions.checkStringNotEmpty(id, "id must not be empty");
         mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
         Bundle bundle = new Bundle();
         bundle.putAll(candidateQueryData);
diff --git a/core/java/android/service/credentials/CallingAppInfo.java b/core/java/android/service/credentials/CallingAppInfo.java
index fba91d4..e755581 100644
--- a/core/java/android/service/credentials/CallingAppInfo.java
+++ b/core/java/android/service/credentials/CallingAppInfo.java
@@ -17,6 +17,7 @@
 package android.service.credentials;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.pm.SigningInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,6 +33,8 @@
 public final class CallingAppInfo implements Parcelable {
     @NonNull private final String mPackageName;
     @NonNull private final SigningInfo mSigningInfo;
+    @Nullable
+    private final String mOrigin;
 
     /**
      * Constructs a new instance.
@@ -41,14 +44,31 @@
      */
     public CallingAppInfo(@NonNull String packageName,
             @NonNull SigningInfo signingInfo) {
+        this(packageName, signingInfo, /*origin=*/ null);
+    }
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param packageName - the package name of the calling app
+     * @param signingInfo - the signing info on the calling app
+     * @param origin - the origin that the calling app wants to use when making request on behalf of
+     *               other
+     * @throws IllegalArgumentException If {@code packageName} is null or empty.
+     * @throws NullPointerException If {@code signingInfo} is null.
+     */
+    public CallingAppInfo(@NonNull String packageName,
+            @NonNull SigningInfo signingInfo, @Nullable String origin) {
         mPackageName = Preconditions.checkStringNotEmpty(packageName, "package name"
                 + "must not be null or empty");
         mSigningInfo = Objects.requireNonNull(signingInfo);
+        mOrigin = origin;
     }
 
     private CallingAppInfo(@NonNull Parcel in) {
         mPackageName = in.readString8();
         mSigningInfo = in.readTypedObject(SigningInfo.CREATOR);
+        mOrigin = in.readString8();
     }
 
     public static final @NonNull Creator<CallingAppInfo> CREATOR = new Creator<CallingAppInfo>() {
@@ -76,6 +96,22 @@
         return mSigningInfo;
     }
 
+    /**
+     * Returns the origin of the calling app if set otherwise returns null.
+     * This value is set only if the origin is different than that of the calling app,
+     * and should be expected from privileged callers(browsers) only when making request on behalf
+     * of other applications.
+     *
+     * Android system makes sure that only applications that poses the permission
+     * {@link android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN} can set the origin on
+     * the incoming {@link android.credentials.GetCredentialRequest} or
+     * {@link android.credentials.CreateCredentialRequest}.
+     */
+    @Nullable
+    public String getOrigin() {
+        return mOrigin;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -85,6 +121,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString8(mPackageName);
         dest.writeTypedObject(mSigningInfo, flags);
+        dest.writeString8(mOrigin);
     }
 
     @Override
@@ -97,6 +134,7 @@
         } else {
             builder.append(", mSigningInfo: null");
         }
+        builder.append(",mOrigin: " + mOrigin);
         builder.append(" }");
         return builder.toString();
     }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 12cd523..f53abce 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -2198,6 +2198,11 @@
                 }
                 mCreated = false;
             }
+
+            if (mSurfaceControl != null) {
+                mSurfaceControl.release();
+                mSurfaceControl = null;
+            }
         }
 
         private final DisplayListener mDisplayListener = new DisplayListener() {
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
index c3ca286..b9ad773 100644
--- a/core/java/android/telephony/CellBroadcastIntents.java
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -104,7 +104,7 @@
      * Put the phone ID and sub ID into an intent as extras.
      */
     private static void putPhoneIdAndSubIdExtra(Context context, Intent intent, int phoneId) {
-        int subId = getSubIdForPhone(context, phoneId);
+        int subId = SubscriptionManager.getSubscriptionId(phoneId);
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             intent.putExtra("subscription", subId);
             intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
@@ -112,22 +112,4 @@
         intent.putExtra("phone", phoneId);
         intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
     }
-
-    /**
-     * Get the subscription ID for a phone ID, or INVALID_SUBSCRIPTION_ID if the phone does not
-     * have an active sub
-     * @param phoneId the phoneId to use
-     * @return the associated sub id
-     */
-    private static int getSubIdForPhone(Context context, int phoneId) {
-        SubscriptionManager subMan =
-                (SubscriptionManager) context.getSystemService(
-                        Context.TELEPHONY_SUBSCRIPTION_SERVICE);
-        int[] subIds = subMan.getSubscriptionIds(phoneId);
-        if (subIds != null) {
-            return subIds[0];
-        } else {
-            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-        }
-    }
 }
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 98c0d7f..5aa0f59 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -477,7 +477,14 @@
         return mTimeResult;
     }
 
-    /** Clears the last received NTP. Intended for use during tests. */
+    /** Sets the last received NTP time. Intended for use during tests. */
+    public void setCachedTimeResult(TimeResult timeResult) {
+        synchronized (this) {
+            mTimeResult = timeResult;
+        }
+    }
+
+    /** Clears the last received NTP time. Intended for use during tests. */
     public void clearCachedTimeResult() {
         synchronized (this) {
             mTimeResult = null;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 20be9d6..5476088 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1261,20 +1261,17 @@
 
     /**
      * @hide
-     * Returns the display's HDR supported types.
+     * Returns the current mode's supported HDR types.
      *
      * @see #isHdr()
-     * @see HdrCapabilities#getSupportedHdrTypes()
+     * @see Mode#getSupportedHdrTypes()
      */
     @TestApi
     @NonNull
     public int[] getReportedHdrTypes() {
         synchronized (mLock) {
             updateDisplayInfoLocked();
-            if (mDisplayInfo.hdrCapabilities == null) {
-                return new int[0];
-            }
-            return mDisplayInfo.hdrCapabilities.getSupportedHdrTypes();
+            return mDisplayInfo.getMode().getSupportedHdrTypes();
         }
     }
 
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1a5613e..ab81345 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1352,6 +1352,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mSource;
     private int mDisplayId = INVALID_DISPLAY;
+    // NOTE: mHmac is private and not used in this class, but it's used on native side / parcel.
     private @Nullable byte[] mHmac;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int mMetaState;
@@ -1377,7 +1378,7 @@
      */
     private long mEventTime;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private String mCharacters;
+    private @Nullable String mCharacters;
 
     public interface Callback {
         /**
@@ -1441,7 +1442,11 @@
     private static native int nativeKeyCodeFromString(String keyCode);
     private static native int nativeNextId();
 
-    private KeyEvent() {}
+    private KeyEvent() {
+        this(/* downTime= */ 0, /* eventTime= */ 0, /* action= */ 0, /* code= */0, /* repeat= */ 0,
+                /* metaState= */ 0, /* deviceId= */ 0, /* scancode= */ 0, /* flags= */ 0,
+                /* source= */ 0, /* characters= */ null);
+    }
 
     /**
      * Create a new key event.
@@ -1451,11 +1456,9 @@
      * @param code The key code.
      */
     public KeyEvent(int action, int code) {
-        mId = nativeNextId();
-        mAction = action;
-        mKeyCode = code;
-        mRepeatCount = 0;
-        mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
+        this(/* downTime= */ 0, /* eventTime= */ 0, action, code, /* repeat= */ 0,
+                /* metaState= */ 0, /* deviceId= */ KeyCharacterMap.VIRTUAL_KEYBOARD,
+                /* scancode= */ 0, /* flags= */ 0, /* source= */ 0, /* characters= */ null);
     }
 
     /**
@@ -1473,13 +1476,9 @@
      */
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat) {
-        mId = nativeNextId();
-        mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
-        mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
-        mAction = action;
-        mKeyCode = code;
-        mRepeatCount = repeat;
-        mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
+        this(downTime, eventTime, action, code, repeat, /* metaState= */ 0,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, /* scancode= */ 0, /* flags= */ 0,
+                /* source= */ 0, /* characters= */ null);
     }
 
     /**
@@ -1498,14 +1497,8 @@
      */
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState) {
-        mId = nativeNextId();
-        mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
-        mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
-        mAction = action;
-        mKeyCode = code;
-        mRepeatCount = repeat;
-        mMetaState = metaState;
-        mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
+        this(downTime, eventTime, action, code, repeat, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD,
+                /* scancode= */ 0, /* flags= */ 0, /* source= */ 0, /* characters= */ null);
     }
 
     /**
@@ -1527,15 +1520,8 @@
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState,
                     int deviceId, int scancode) {
-        mId = nativeNextId();
-        mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
-        mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
-        mAction = action;
-        mKeyCode = code;
-        mRepeatCount = repeat;
-        mMetaState = metaState;
-        mDeviceId = deviceId;
-        mScanCode = scancode;
+        this(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
+                /* flags= */ 0, /* source= */ 0, /* characters= */ null);
     }
 
     /**
@@ -1558,16 +1544,8 @@
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState,
                     int deviceId, int scancode, int flags) {
-        mId = nativeNextId();
-        mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
-        mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
-        mAction = action;
-        mKeyCode = code;
-        mRepeatCount = repeat;
-        mMetaState = metaState;
-        mDeviceId = deviceId;
-        mScanCode = scancode;
-        mFlags = flags;
+        this(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, flags,
+                /* source= */ 0, /* characters= */ null);
     }
 
     /**
@@ -1591,6 +1569,14 @@
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState,
                     int deviceId, int scancode, int flags, int source) {
+        this(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, flags,
+                source, /* characters= */ null);
+    }
+
+    private KeyEvent(long downTime, long eventTime, int action, int code, int repeat, int metaState,
+            int deviceId, int scancode, int flags, int source,  @Nullable String characters) {
+        // NOTE: this is the canonical constructor, other constructors that takes KeyEvent
+        // attributes should call it
         mId = nativeNextId();
         mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
         mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
@@ -1602,6 +1588,7 @@
         mScanCode = scancode;
         mFlags = flags;
         mSource = source;
+        mCharacters = characters;
     }
 
     /**
@@ -1617,36 +1604,18 @@
      * @param flags The flags for this key event
      */
     public KeyEvent(long time, String characters, int deviceId, int flags) {
-        mId = nativeNextId();
-        mDownTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);
-        mEventTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);
-        mCharacters = characters;
-        mAction = ACTION_MULTIPLE;
-        mKeyCode = KEYCODE_UNKNOWN;
-        mRepeatCount = 0;
-        mDeviceId = deviceId;
-        mFlags = flags;
-        mSource = InputDevice.SOURCE_KEYBOARD;
+        this(/* downTime= */ time, /* eventTime= */ time, ACTION_MULTIPLE, KEYCODE_UNKNOWN,
+                /* repeat= */ 0, /* metaState= */ 0, deviceId, /* scancode= */ 0, flags,
+                /* source= */ InputDevice.SOURCE_KEYBOARD, characters);
     }
 
     /**
      * Make an exact copy of an existing key event.
      */
     public KeyEvent(KeyEvent origEvent) {
-        mId = origEvent.mId;
-        mDownTime = origEvent.mDownTime;
-        mEventTime = origEvent.mEventTime;
-        mAction = origEvent.mAction;
-        mKeyCode = origEvent.mKeyCode;
-        mRepeatCount = origEvent.mRepeatCount;
-        mMetaState = origEvent.mMetaState;
-        mDeviceId = origEvent.mDeviceId;
-        mSource = origEvent.mSource;
-        mDisplayId = origEvent.mDisplayId;
-        mHmac = origEvent.mHmac == null ? null : origEvent.mHmac.clone();
-        mScanCode = origEvent.mScanCode;
-        mFlags = origEvent.mFlags;
-        mCharacters = origEvent.mCharacters;
+        this(origEvent, origEvent.mId, origEvent.mEventTime, origEvent.mAction,
+                origEvent.mRepeatCount, origEvent.mHmac == null ? null : origEvent.mHmac.clone(),
+                origEvent.mCharacters);
     }
 
     /**
@@ -1662,20 +1631,30 @@
      */
     @Deprecated
     public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
-        mId = nativeNextId();  // Not an exact copy so assign a new ID.
+        // Not an exact copy so assign a new ID.
+        // Don't copy HMAC, it will be invalid because eventTime is changing
+        this(origEvent, nativeNextId(),
+                TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS), origEvent.mAction,
+                newRepeat, /* hmac= */ null, origEvent.mCharacters);
+    }
+
+    // This is the canonical constructor that should be called for constructors that take a KeyEvent
+    private KeyEvent(KeyEvent origEvent, int id, long eventTime, int action, int newRepeat,
+            @Nullable byte[] hmac, @Nullable String characters) {
+        mId = id;
         mDownTime = origEvent.mDownTime;
-        mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
-        mAction = origEvent.mAction;
+        mEventTime = eventTime;
+        mAction = action;
         mKeyCode = origEvent.mKeyCode;
         mRepeatCount = newRepeat;
         mMetaState = origEvent.mMetaState;
         mDeviceId = origEvent.mDeviceId;
         mSource = origEvent.mSource;
         mDisplayId = origEvent.mDisplayId;
-        mHmac = null; // Don't copy HMAC, it will be invalid because eventTime is changing
+        mHmac = hmac;
         mScanCode = origEvent.mScanCode;
         mFlags = origEvent.mFlags;
-        mCharacters = origEvent.mCharacters;
+        mCharacters = characters;
     }
 
     private static KeyEvent obtain() {
@@ -1857,21 +1836,11 @@
      * @param action The new action code of the event.
      */
     private KeyEvent(KeyEvent origEvent, int action) {
-        mId = nativeNextId();  // Not an exact copy so assign a new ID.
-        mDownTime = origEvent.mDownTime;
-        mEventTime = origEvent.mEventTime;
-        mAction = action;
-        mKeyCode = origEvent.mKeyCode;
-        mRepeatCount = origEvent.mRepeatCount;
-        mMetaState = origEvent.mMetaState;
-        mDeviceId = origEvent.mDeviceId;
-        mSource = origEvent.mSource;
-        mDisplayId = origEvent.mDisplayId;
-        mHmac = null; // Don't copy the hmac, it will be invalid since action is changing
-        mScanCode = origEvent.mScanCode;
-        mFlags = origEvent.mFlags;
-        // Don't copy mCharacters, since one way or the other we'll lose it
-        // when changing the action.
+        // Not an exact copy so assign a new ID
+        // Don't copy the hmac, it will be invalid since action is changing
+        // Don't copy mCharacters, since one way or the other we'll lose it when changing action.
+        this(origEvent, nativeNextId(), origEvent.mEventTime, action, origEvent.mRepeatCount,
+                /* hmac= */ null, /* characters= */ null);
     }
 
     /**
@@ -3219,6 +3188,8 @@
     }
 
     private KeyEvent(Parcel in) {
+        // NOTE: ideally this constructor should call the canonical one, but that would require
+        // changing the order the fields are written to the parcel, which could break native code
         mId = in.readInt();
         mDeviceId = in.readInt();
         mSource = in.readInt();
diff --git a/core/java/android/view/accessibility/DirectAccessibilityConnection.java b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
index 8a3bb6f..25f5b8c 100644
--- a/core/java/android/view/accessibility/DirectAccessibilityConnection.java
+++ b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
@@ -50,13 +50,13 @@
 class DirectAccessibilityConnection extends IAccessibilityServiceConnection.Default {
     private final IAccessibilityInteractionConnection mAccessibilityInteractionConnection;
     private final AccessibilityManager mAccessibilityManager;
+    private final int mMyProcessId;
 
     // Fetch all views, but do not use prefetching/cache since this "connection" does not
     // receive cache invalidation events (as it is not linked to an AccessibilityService).
     private static final int FETCH_FLAGS =
             AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
                     | AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
-    private static final int PID = Process.myPid();
     private static final Region INTERACTIVE_REGION = null;
 
     DirectAccessibilityConnection(
@@ -64,6 +64,7 @@
             AccessibilityManager accessibilityManager) {
         mAccessibilityInteractionConnection = accessibilityInteractionConnection;
         mAccessibilityManager = accessibilityManager;
+        mMyProcessId = Process.myPid();
     }
 
     @Override
@@ -74,8 +75,9 @@
         IAccessibilityManager.WindowTransformationSpec spec =
                 mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
         mAccessibilityInteractionConnection.findAccessibilityNodeInfoByAccessibilityId(
-                accessibilityNodeId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID,
-                threadId, spec.magnificationSpec, spec.transformationMatrix, arguments);
+                accessibilityNodeId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS,
+                mMyProcessId, threadId, spec.magnificationSpec, spec.transformationMatrix,
+                arguments);
         return new String[0];
     }
 
@@ -87,8 +89,8 @@
         IAccessibilityManager.WindowTransformationSpec spec =
                 mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
         mAccessibilityInteractionConnection.findAccessibilityNodeInfosByText(accessibilityNodeId,
-                text, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
-                spec.magnificationSpec, spec.transformationMatrix);
+                text, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, mMyProcessId,
+                threadId, spec.magnificationSpec, spec.transformationMatrix);
         return new String[0];
     }
 
@@ -100,8 +102,8 @@
         IAccessibilityManager.WindowTransformationSpec spec =
                 mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
         mAccessibilityInteractionConnection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
-                viewId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
-                spec.magnificationSpec, spec.transformationMatrix);
+                viewId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, mMyProcessId,
+                threadId, spec.magnificationSpec, spec.transformationMatrix);
         return new String[0];
     }
 
@@ -112,7 +114,7 @@
         IAccessibilityManager.WindowTransformationSpec spec =
                 mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
         mAccessibilityInteractionConnection.findFocus(accessibilityNodeId, focusType,
-                INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
+                INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, mMyProcessId, threadId,
                 spec.magnificationSpec, spec.transformationMatrix);
         return new String[0];
     }
@@ -124,7 +126,7 @@
         IAccessibilityManager.WindowTransformationSpec spec =
                 mAccessibilityManager.getWindowTransformationSpec(accessibilityWindowId);
         mAccessibilityInteractionConnection.focusSearch(accessibilityNodeId, direction,
-                INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
+                INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, mMyProcessId, threadId,
                 spec.magnificationSpec, spec.transformationMatrix);
         return new String[0];
     }
@@ -135,7 +137,7 @@
             IAccessibilityInteractionConnectionCallback callback, long threadId)
             throws RemoteException {
         mAccessibilityInteractionConnection.performAccessibilityAction(accessibilityNodeId, action,
-                arguments, interactionId, callback, FETCH_FLAGS, PID, threadId);
+                arguments, interactionId, callback, FETCH_FLAGS, mMyProcessId, threadId);
         return true;
     }
 }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bdc7333..aef0e65 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -899,9 +899,10 @@
 
         // 3. Get the activity names substring between the indexes
         final int activityStringStartIndex = packageInStringIndex + packageName.length() + 1;
-        if (activityStringStartIndex < firstNextSemicolonIndex) {
+        if (activityStringStartIndex >= firstNextSemicolonIndex) {
             Log.e(TAG, "Failed to get denied activity names from denylist because it's wrongly "
                     + "formatted");
+            return;
         }
         final String activitySubstring =
                 denyListString.substring(activityStringStartIndex, firstNextSemicolonIndex);
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index db17a53..d84acc0 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -25,6 +25,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -33,6 +34,7 @@
 import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IImeTracker;
 import com.android.internal.inputmethod.IInputMethodClient;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
 import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -40,7 +42,6 @@
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
-import com.android.internal.view.IImeTracker;
 import com.android.internal.view.IInputMethodManager;
 
 import java.util.ArrayList;
@@ -581,51 +582,57 @@
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onRequestShow */
     @AnyThread
-    @Nullable
-    static IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
-            @SoftInputShowHideReason int reason) {
+    @NonNull
+    static ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+            @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
-            return null;
+            // Create token with "fake" binder if the service was not found.
+            return new ImeTracker.Token(new Binder(), tag);
         }
         try {
-            return service.onRequestShow(uid, origin, reason);
+            return service.onRequestShow(tag, uid, origin, reason);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onRequestHide */
     @AnyThread
-    @Nullable
-    static IBinder onRequestHide(int uid, @ImeTracker.Origin int origin,
-            @SoftInputShowHideReason int reason) {
+    @NonNull
+    static ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+            @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
-            return null;
+            // Create token with "fake" binder if the service was not found.
+            return new ImeTracker.Token(new Binder(), tag);
         }
         try {
-            return service.onRequestHide(uid, origin, reason);
+            return service.onRequestHide(tag, uid, origin, reason);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onProgress */
     @AnyThread
-    static void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+    static void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
             return;
         }
         try {
-            service.onProgress(statsToken, phase);
+            service.onProgress(binder, phase);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onFailed */
     @AnyThread
-    static void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+    static void onFailed(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
             return;
@@ -637,8 +644,9 @@
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onCancelled */
     @AnyThread
-    static void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+    static void onCancelled(@NonNull ImeTracker.Token statsToken, @ImeTracker.Phase int phase) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
             return;
@@ -650,8 +658,9 @@
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onShown */
     @AnyThread
-    static void onShown(@NonNull IBinder statsToken) {
+    static void onShown(@NonNull ImeTracker.Token statsToken) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
             return;
@@ -663,8 +672,9 @@
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#onHidden */
     @AnyThread
-    static void onHidden(@NonNull IBinder statsToken) {
+    static void onHidden(@NonNull ImeTracker.Token statsToken) {
         final IImeTracker service = getImeTrackerService();
         if (service == null) {
             return;
@@ -676,6 +686,7 @@
         }
     }
 
+    /** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
     @AnyThread
     @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
     static boolean hasPendingImeVisibilityRequests() {
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index e5a99ff..f0d1019 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -26,7 +26,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.content.Context;
-import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -47,7 +46,7 @@
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.stream.Collectors;
 
 /** @hide */
@@ -321,9 +320,8 @@
     /**
      * Creates an IME show request tracking token.
      *
-     * @param component the component name where the IME show request was created,
-     *                  or {@code null} otherwise
-     *                  (defaulting to {@link ActivityThread#currentProcessName()}).
+     * @param component the name of the component that created the IME request, or {@code null}
+     *                  otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
      * @param uid the uid of the client that requested the IME.
      * @param origin the origin of the IME show request.
      * @param reason the reason why the IME show request was created.
@@ -337,9 +335,8 @@
     /**
      * Creates an IME hide request tracking token.
      *
-     * @param component the component name where the IME hide request was created,
-     *                  or {@code null} otherwise
-     *                  (defaulting to {@link ActivityThread#currentProcessName()}).
+     * @param component the name of the component that created the IME request, or {@code null}
+     *                  otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
      * @param uid the uid of the client that requested the IME.
      * @param origin the origin of the IME hide request.
      * @param reason the reason why the IME hide request was created.
@@ -435,8 +432,7 @@
             mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false);
             // Update logging flag dynamically.
             SystemProperties.addChangeCallback(() ->
-                    mLogProgress =
-                            SystemProperties.getBoolean("persist.debug.imetracker", false));
+                    mLogProgress = SystemProperties.getBoolean("persist.debug.imetracker", false));
         }
 
         /** Whether progress should be logged. */
@@ -446,10 +442,9 @@
         @Override
         public Token onRequestShow(@Nullable String component, int uid, @Origin int origin,
                 @SoftInputShowHideReason int reason) {
-            IBinder binder = IInputMethodManagerGlobalInvoker.onRequestShow(uid, origin, reason);
-            if (binder == null) binder = new Binder();
-
-            final Token token = Token.build(binder, component);
+            final var tag = getTag(component);
+            final var token = IInputMethodManagerGlobalInvoker.onRequestShow(tag, uid, origin,
+                    reason);
 
             Log.i(TAG, token.mTag + ": onRequestShow at " + Debug.originToString(origin)
                     + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
@@ -461,10 +456,9 @@
         @Override
         public Token onRequestHide(@Nullable String component, int uid, @Origin int origin,
                 @SoftInputShowHideReason int reason) {
-            IBinder binder = IInputMethodManagerGlobalInvoker.onRequestHide(uid, origin, reason);
-            if (binder == null) binder = new Binder();
-
-            final Token token = Token.build(binder, component);
+            final var tag = getTag(component);
+            final var token = IInputMethodManagerGlobalInvoker.onRequestHide(tag, uid, origin,
+                    reason);
 
             Log.i(TAG, token.mTag + ": onRequestHide at " + Debug.originToString(origin)
                     + " reason " + InputMethodDebug.softInputDisplayReasonToString(reason));
@@ -485,7 +479,7 @@
         @Override
         public void onFailed(@Nullable Token token, @Phase int phase) {
             if (token == null) return;
-            IInputMethodManagerGlobalInvoker.onFailed(token.mBinder, phase);
+            IInputMethodManagerGlobalInvoker.onFailed(token, phase);
 
             Log.i(TAG, token.mTag + ": onFailed at " + Debug.phaseToString(phase));
         }
@@ -499,7 +493,7 @@
         @Override
         public void onCancelled(@Nullable Token token, @Phase int phase) {
             if (token == null) return;
-            IInputMethodManagerGlobalInvoker.onCancelled(token.mBinder, phase);
+            IInputMethodManagerGlobalInvoker.onCancelled(token, phase);
 
             Log.i(TAG, token.mTag + ": onCancelled at " + Debug.phaseToString(phase));
         }
@@ -507,7 +501,7 @@
         @Override
         public void onShown(@Nullable Token token) {
             if (token == null) return;
-            IInputMethodManagerGlobalInvoker.onShown(token.mBinder);
+            IInputMethodManagerGlobalInvoker.onShown(token);
 
             Log.i(TAG, token.mTag + ": onShown");
         }
@@ -515,10 +509,24 @@
         @Override
         public void onHidden(@Nullable Token token) {
             if (token == null) return;
-            IInputMethodManagerGlobalInvoker.onHidden(token.mBinder);
+            IInputMethodManagerGlobalInvoker.onHidden(token);
 
             Log.i(TAG, token.mTag + ": onHidden");
         }
+
+        /**
+         * Returns a logging tag using the given component name.
+         *
+         * @param component the name of the component that created the IME request, or {@code null}
+         *                  otherwise (defaulting to {@link ActivityThread#currentProcessName()}).
+         */
+        @NonNull
+        private String getTag(@Nullable String component) {
+            if (component == null) {
+                component = ActivityThread.currentProcessName();
+            }
+            return component + ":" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
+        }
     };
 
     /** The singleton IME tracker instance for instrumenting jank metrics. */
@@ -528,28 +536,31 @@
     ImeLatencyTracker LATENCY_TRACKER = new ImeLatencyTracker();
 
     /** A token that tracks the progress of an IME request. */
-    class Token implements Parcelable {
+    final class Token implements Parcelable {
 
+        /** The binder used to identify this token. */
         @NonNull
-        public final IBinder mBinder;
+        private final IBinder mBinder;
 
+        /** Logging tag, of the shape "component:random_hexadecimal". */
         @NonNull
         private final String mTag;
 
-        @NonNull
-        private static Token build(@NonNull IBinder binder, @Nullable String component) {
-            if (component == null) component = ActivityThread.currentProcessName();
-            final String tag = component + ":" + Integer.toHexString((new Random().nextInt()));
-
-            return new Token(binder, tag);
-        }
-
-        private Token(@NonNull IBinder binder, @NonNull String tag) {
+        public Token(@NonNull IBinder binder, @NonNull String tag) {
             mBinder = binder;
             mTag = tag;
         }
 
-        /** Returns the {@link Token#mTag} */
+        private Token(@NonNull Parcel in) {
+            mBinder = in.readStrongBinder();
+            mTag = in.readString8();
+        }
+
+        @NonNull
+        public IBinder getBinder() {
+            return mBinder;
+        }
+
         @NonNull
         public String getTag() {
             return mTag;
@@ -562,7 +573,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeStrongBinder(mBinder);
             dest.writeString8(mTag);
         }
@@ -571,12 +582,11 @@
         public static final Creator<Token> CREATOR = new Creator<>() {
             @NonNull
             @Override
-            public Token createFromParcel(Parcel source) {
-                final IBinder binder = source.readStrongBinder();
-                final String tag = source.readString8();
-                return new Token(binder, tag);
+            public Token createFromParcel(@NonNull Parcel in) {
+                return new Token(in);
             }
 
+            @NonNull
             @Override
             public Token[] newArray(int size) {
                 return new Token[size];
@@ -589,40 +599,50 @@
      *
      * Note: This is held in a separate class so that it only gets initialized when actually needed.
      */
-    class Debug {
+    final class Debug {
 
+        @NonNull
         private static final Map<Integer, String> sTypes =
                 getFieldMapping(ImeTracker.class, "TYPE_");
+        @NonNull
         private static final Map<Integer, String> sStatus =
                 getFieldMapping(ImeTracker.class, "STATUS_");
+        @NonNull
         private static final Map<Integer, String> sOrigins =
                 getFieldMapping(ImeTracker.class, "ORIGIN_");
+        @NonNull
         private static final Map<Integer, String> sPhases =
                 getFieldMapping(ImeTracker.class, "PHASE_");
 
+        @NonNull
         public static String typeToString(@Type int type) {
             return sTypes.getOrDefault(type, "TYPE_" + type);
         }
 
+        @NonNull
         public static String statusToString(@Status int status) {
             return sStatus.getOrDefault(status, "STATUS_" + status);
         }
 
+        @NonNull
         public static String originToString(@Origin int origin) {
             return sOrigins.getOrDefault(origin, "ORIGIN_" + origin);
         }
 
+        @NonNull
         public static String phaseToString(@Phase int phase) {
             return sPhases.getOrDefault(phase, "PHASE_" + phase);
         }
 
-        private static Map<Integer, String> getFieldMapping(Class<?> cls, String fieldPrefix) {
+        @NonNull
+        private static Map<Integer, String> getFieldMapping(Class<?> cls,
+                @NonNull String fieldPrefix) {
             return Arrays.stream(cls.getDeclaredFields())
                     .filter(field -> field.getName().startsWith(fieldPrefix))
                     .collect(Collectors.toMap(Debug::getFieldValue, Field::getName));
         }
 
-        private static int getFieldValue(Field field) {
+        private static int getFieldValue(@NonNull Field field) {
             try {
                 return field.getInt(null);
             } catch (IllegalAccessException e) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b89dd31..9f9a781 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -52,6 +52,7 @@
 import android.graphics.RenderNode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -3238,6 +3239,44 @@
                 .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
 
         mPreserveSelection = true;
+
+        // No-op for the old context menu because it doesn't have icons.
+        adjustIconSpacing(menu);
+    }
+
+    /**
+     * Adjust icon spacing to align the texts.
+     * @hide
+     */
+    @VisibleForTesting
+    public void adjustIconSpacing(ContextMenu menu) {
+        int width = -1;
+        int height = -1;
+        for (int i = 0; i < menu.size(); ++i) {
+            final MenuItem item = menu.getItem(i);
+            final Drawable d = item.getIcon();
+            if (d == null) {
+                continue;
+            }
+
+            width = Math.max(width, d.getIntrinsicWidth());
+            height = Math.max(height, d.getIntrinsicHeight());
+        }
+
+        if (width < 0 || height < 0) {
+            return;  // No menu has icon drawable.
+        }
+
+        GradientDrawable paddingDrawable = new GradientDrawable();
+        paddingDrawable.setSize(width, height);
+
+        for (int i = 0; i < menu.size(); ++i) {
+            final MenuItem item = menu.getItem(i);
+            final Drawable d = item.getIcon();
+            if (d == null) {
+                item.setIcon(paddingDrawable);
+            }
+        }
     }
 
     @Nullable
diff --git a/core/java/com/android/internal/view/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
similarity index 71%
rename from core/java/com/android/internal/view/IImeTracker.aidl
rename to core/java/com/android/internal/inputmethod/IImeTracker.aidl
index b062ca7..c7418ee 100644
--- a/core/java/com/android/internal/view/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -14,43 +14,45 @@
  * limitations under the License.
  */
 
-package com.android.internal.view;
+package com.android.internal.inputmethod;
 
 import android.view.inputmethod.ImeTracker;
 
 /**
- * Interface to the global Ime tracker, used by all client applications.
+ * Interface to the global IME tracker service, used by all client applications.
  * {@hide}
  */
 interface IImeTracker {
 
     /**
-     * Called when an IME show request is created,
-     * returns a new Binder to be associated with the IME tracking token.
+     * Called when an IME show request is created.
      *
+     * @param tag the logging tag.
      * @param uid the uid of the client that requested the IME.
      * @param origin the origin of the IME show request.
      * @param reason the reason why the IME show request was created.
+     * @return A new IME tracking token.
      */
-    IBinder onRequestShow(int uid, int origin, int reason);
+    ImeTracker.Token onRequestShow(String tag, int uid, int origin, int reason);
 
     /**
-     * Called when an IME hide request is created,
-     * returns a new Binder to be associated with the IME tracking token.
+     * Called when an IME hide request is created.
      *
+     * @param tag the logging tag.
      * @param uid the uid of the client that requested the IME.
      * @param origin the origin of the IME hide request.
      * @param reason the reason why the IME hide request was created.
+     * @return A new IME tracking token.
      */
-    IBinder onRequestHide(int uid, int origin, int reason);
+    ImeTracker.Token onRequestHide(String tag, int uid, int origin, int reason);
 
     /**
      * Called when the IME request progresses to a further phase.
      *
-     * @param statsToken the token tracking the current IME request.
+     * @param binder the binder of token tracking the current IME request.
      * @param phase the new phase the IME request reached.
      */
-    oneway void onProgress(in IBinder statsToken, int phase);
+    oneway void onProgress(in IBinder binder, int phase);
 
     /**
      * Called when the IME request fails.
@@ -58,7 +60,7 @@
      * @param statsToken the token tracking the current IME request.
      * @param phase the phase the IME request failed at.
      */
-    oneway void onFailed(in IBinder statsToken, int phase);
+    oneway void onFailed(in ImeTracker.Token statsToken, int phase);
 
     /**
      * Called when the IME request is cancelled.
@@ -66,21 +68,21 @@
      * @param statsToken the token tracking the current IME request.
      * @param phase the phase the IME request was cancelled at.
      */
-    oneway void onCancelled(in IBinder statsToken, int phase);
+    oneway void onCancelled(in ImeTracker.Token statsToken, int phase);
 
     /**
      * Called when the IME show request is successful.
      *
      * @param statsToken the token tracking the current IME request.
      */
-    oneway void onShown(in IBinder statsToken);
+    oneway void onShown(in ImeTracker.Token statsToken);
 
     /**
      * Called when the IME hide request is successful.
      *
      * @param statsToken the token tracking the current IME request.
      */
-    oneway void onHidden(in IBinder statsToken);
+    oneway void onHidden(in ImeTracker.Token statsToken);
 
     /**
      * Checks whether there are any pending IME visibility requests.
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5805d0e..9a4610e 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -23,11 +23,11 @@
 import android.view.inputmethod.EditorInfo;
 import android.window.ImeOnBackInvokedDispatcher;
 
+import com.android.internal.inputmethod.IImeTracker;
 import com.android.internal.inputmethod.IInputMethodClient;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
 import com.android.internal.inputmethod.IRemoteInputConnection;
 import com.android.internal.inputmethod.InputBindResult;
-import com.android.internal.view.IImeTracker;
 
 /**
  * Public interface to the global input method manager, used by all client
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d2ee5de..1bbe8ee 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1714,7 +1714,7 @@
             this type.
         -->
         <flag name="remoteMessaging" value="0x200" />
-        <!-- The system exmpted foreground service use cases.
+        <!-- The system exempted foreground service use cases.
             <p>Requires the app to hold the permission
             {@link android.Manifest.permission#FOREGROUND_SERVICE_SYSTEM_EXEMPTED} in order to use
             this type. Apps are allowed to use this type only in the use cases listed in
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 12eff67..2f94ed7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3065,6 +3065,10 @@
     <string name="config_credentialManagerDialogComponent" translatable="false"
             >com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string>
 
+    <!-- Name of the broadcast receiver that is used to receive provider change events -->
+    <string name="config_credentialManagerReceiverComponent" translatable="false"
+            >com.android.credentialmanager/com.android.credentialmanager.CredentialProviderReceiver</string>
+
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e89fc8..12646a0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2210,6 +2210,7 @@
   <java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
   <java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
   <java-symbol type="string" name="config_credentialManagerDialogComponent" />
+  <java-symbol type="string" name="config_credentialManagerReceiverComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
   <java-symbol type="string" name="config_persistentDataPackageName" />
   <java-symbol type="string" name="config_deviceConfiguratorPackageName" />
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index 55ef854..980211f 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
+import android.util.PollingCheck;
 import android.view.View;
 import android.widget.TextView;
 
@@ -91,6 +92,9 @@
 
         var densityRef = new AtomicReference<Float>();
         scenario.onActivity(activity -> {
+            assertThat(activity.getResources().getConfiguration().fontScale)
+                .isWithin(0.05f)
+                .of(2f);
             densityRef.compareAndSet(null, activity.getResources().getDisplayMetrics().density);
         });
         var density = densityRef.get();
@@ -141,6 +145,15 @@
                     fontScale
             );
         });
+
+        PollingCheck.waitFor(/* timeout= */ 5000, () ->
+                InstrumentationRegistry
+                    .getInstrumentation()
+                    .getContext()
+                    .getResources()
+                    .getConfiguration()
+                    .fontScale == fontScale
+        );
     }
 
     private Matcher<View> withTextSizeInRange(float sizeStartPx, float sizeEndPx) {
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index 249e246..ef106bc 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -90,11 +90,7 @@
             }
             .forEach { (table, sp) ->
                 try {
-                    assertWithMessage(
-                        "convertSpToDp(%s) on table: %s",
-                        sp.toString(),
-                        table.toString()
-                    )
+                    assertWithMessage("convertSpToDp(%s) on table: %s", sp, table)
                         .that(table.convertSpToDp(sp))
                         .isFinite()
                 } catch (e: Exception) {
diff --git a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
index b533b47..25664fb 100644
--- a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
+++ b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
@@ -101,8 +101,11 @@
         mGetRequest = new GetCredentialRequest.Builder(Bundle.EMPTY).addCredentialOption(
                 new CredentialOption(Credential.TYPE_PASSWORD_CREDENTIAL, Bundle.EMPTY,
                         Bundle.EMPTY, false)).build();
-        mCreateRequest = new CreateCredentialRequest(Credential.TYPE_PASSWORD_CREDENTIAL,
-                Bundle.EMPTY, Bundle.EMPTY, false, false);
+        mCreateRequest = new CreateCredentialRequest.Builder(Bundle.EMPTY, Bundle.EMPTY)
+                .setType(Credential.TYPE_PASSWORD_CREDENTIAL)
+                .setIsSystemProviderRequired(false)
+                .setAlwaysSendAppInfoToProvider(false)
+                .build();
         mClearRequest = new ClearCredentialStateRequest(Bundle.EMPTY);
 
         final Slice slice = new Slice.Builder(Uri.parse("foo://bar"), null).addText("some text",
diff --git a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
new file mode 100644
index 0000000..42c97f3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.os;
+
+import static android.os.CancellationSignalBeamer.Sender;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.CancellationSignalBeamer.Receiver;
+import android.util.PollingCheck;
+import android.util.PollingCheck.PollingCheckCondition;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+import java.util.concurrent.CountDownLatch;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CancellationSignalBeamerTest {
+
+    private CancellationSignal mSenderSignal = new CancellationSignal();
+    private CancellationSignal mReceivedSignal;
+    private Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+    @Test
+    public void testBeam_null() {
+        try (var token = mSender.beam(null)) {
+            assertThat(token).isNull();
+            invokeGenericService(token);
+        }
+        assertThat(mReceivedSignal).isNull();
+    }
+
+    @Test
+    public void testBeam_nonNull() {
+        try (var token = mSender.beam(mSenderSignal)) {
+            assertThat(token).isNotNull();
+            invokeGenericService(token);
+        }
+        assertThat(mReceivedSignal).isNotNull();
+    }
+
+    @Test
+    public void testBeam_async() {
+        IBinder outerToken;
+        try (var token = mSender.beam(mSenderSignal)) {
+            assertThat(token).isNotNull();
+            outerToken = token;
+        }
+        invokeGenericService(outerToken);
+        assertThat(mReceivedSignal).isNotNull();
+    }
+
+    @Test
+    public void testCancelOnSentSignal_cancelsReceivedSignal() {
+        try (var token = mSender.beam(mSenderSignal)) {
+            invokeGenericService(token);
+        }
+        mSenderSignal.cancel();
+        assertThat(mReceivedSignal.isCanceled()).isTrue();
+    }
+
+    @Test
+    public void testSendingCancelledSignal_cancelsReceivedSignal() {
+        mSenderSignal.cancel();
+        try (var token = mSender.beam(mSenderSignal)) {
+            invokeGenericService(token);
+        }
+        assertThat(mReceivedSignal.isCanceled()).isTrue();
+    }
+
+    @Test
+    public void testUnbeam_null() {
+        assertThat(mReceiver.unbeam(null)).isNull();
+    }
+
+    @Test
+    public void testForget_null() {
+        mReceiver.forget(null);
+    }
+
+    @Test
+    public void testCancel_null() {
+        mReceiver.cancel(null);
+    }
+
+    @Test
+    public void testForget_withUnknownToken() {
+        mReceiver.forget(new Binder());
+    }
+
+    @Test
+    public void testCancel_withUnknownToken() {
+        mReceiver.cancel(new Binder());
+    }
+
+    @Test
+    public void testBinderDied_withUnknownToken() {
+        mReceiver.binderDied(new Binder());
+    }
+
+    @Test
+    public void testReceiverWithCancelOnSenderDead_cancelsOnSenderDeath() {
+        var receiver = new Receiver(true /* cancelOnSenderDeath */);
+        var token = new Binder();
+        var signal = receiver.unbeam(token);
+        receiver.binderDied(token);
+        assertThat(signal.isCanceled()).isTrue();
+    }
+
+    @Test
+    public void testReceiverWithoutCancelOnSenderDead_doesntCancelOnSenderDeath() {
+        var receiver = new Receiver(false /* cancelOnSenderDeath */);
+        var token = new Binder();
+        var signal = receiver.unbeam(token);
+        receiver.binderDied(token);
+        assertThat(signal.isCanceled()).isFalse();
+    }
+
+    @Test
+    public void testDroppingSentSignal_dropsReceivedSignal() throws Exception {
+        // In a multiprocess scenario, sending token over Binder might leak the token
+        // on both ends if we create a reference cycle. Simulate that worst-case scenario
+        // here by leaking it directly, then test that cleanup of the signals still works.
+        var receivedSignalCleaned = new CountDownLatch(1);
+        var tokenRef = new Object[1];
+        // Reference the cancellation signals in a separate method scope, so we don't
+        // accidentally leak them on the stack / in a register.
+        Runnable r = () -> {
+            try (var token = mSender.beam(mSenderSignal)) {
+                tokenRef[0] = token;
+                invokeGenericService(token);
+            }
+            mSenderSignal = null;
+
+            Cleaner.create().register(mReceivedSignal, receivedSignalCleaned::countDown);
+            mReceivedSignal = null;
+        };
+        r.run();
+
+        waitForWithGc(() -> receivedSignalCleaned.getCount() == 0);
+
+        Reference.reachabilityFence(tokenRef[0]);
+    }
+
+    @Test
+    public void testRepeatedBeaming_doesntLeak() throws Exception {
+        var receivedSignalCleaned = new CountDownLatch(1);
+        var tokenRef = new Object[1];
+        // Reference the cancellation signals in a separate method scope, so we don't
+        // accidentally leak them on the stack / in a register.
+        Runnable r = () -> {
+            try (var token = mSender.beam(mSenderSignal)) {
+                tokenRef[0] = token;
+                invokeGenericService(token);
+            }
+            // Beaming again leaves mReceivedSignal dangling, so it should be collected.
+            mSender.beam(mSenderSignal).close();
+
+            Cleaner.create().register(mReceivedSignal, receivedSignalCleaned::countDown);
+            mReceivedSignal = null;
+        };
+        r.run();
+
+        waitForWithGc(() -> receivedSignalCleaned.getCount() == 0);
+
+        Reference.reachabilityFence(tokenRef[0]);
+    }
+
+    private void waitForWithGc(PollingCheckCondition condition) throws IOException {
+        try {
+            PollingCheck.waitFor(() -> {
+                Runtime.getRuntime().gc();
+                return condition.canProceed();
+            });
+        } catch (AssertionError e) {
+            File heap = new File(mContext.getExternalFilesDir(null), "dump.hprof");
+            Debug.dumpHprofData(heap.getAbsolutePath());
+            throw e;
+        }
+    }
+
+    private void invokeGenericService(IBinder cancellationSignalToken) {
+        mReceivedSignal = mReceiver.unbeam(cancellationSignalToken);
+    }
+
+    private final Sender mSender = new Sender() {
+        @Override
+        public void onCancel(IBinder token) {
+            mReceiver.cancel(token);
+        }
+
+        @Override
+        public void onForget(IBinder token) {
+            mReceiver.forget(token);
+        }
+    };
+
+    private final Receiver mReceiver = new Receiver(false);
+}
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index b6fc137..2e31bb5 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -32,18 +32,16 @@
 import android.test.mock.MockContentResolver;
 import android.util.MemoryIntArray;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -61,63 +59,42 @@
     private static final String NAMESPACE = "namespace";
     private static final String NAMESPACE2 = "namespace2";
 
-    private static final String SETTING = "test_setting";
-    private static final String SETTING2 = "test_setting2";
-
     @Mock
     private IContentProvider mMockIContentProvider;
     @Mock
     private ContentProvider mMockContentProvider;
     private MockContentResolver mMockContentResolver;
-    private MemoryIntArray mConfigsCacheGenerationStore;
-    private MemoryIntArray mSettingsCacheGenerationStore;
+    private MemoryIntArray mCacheGenerationStore;
+    private int mCurrentGeneration = 123;
 
-    private HashMap<String, HashMap<String, String>> mConfigsStorage;
-    private HashMap<String, String> mSettingsStorage;
-
+    private HashMap<String, HashMap<String, String>> mStorage;
 
     @Before
     public void setUp() throws Exception {
         Settings.Config.clearProviderForTest();
-        Settings.Secure.clearProviderForTest();
         MockitoAnnotations.initMocks(this);
         when(mMockContentProvider.getIContentProvider()).thenReturn(mMockIContentProvider);
-        mMockContentResolver = new MockContentResolver(
-                InstrumentationRegistry.getInstrumentation().getContext());
+        mMockContentResolver = new MockContentResolver(InstrumentationRegistry
+                .getInstrumentation().getContext());
         mMockContentResolver.addProvider(Settings.Config.CONTENT_URI.getAuthority(),
                 mMockContentProvider);
-        mMockContentResolver.addProvider(Settings.Secure.CONTENT_URI.getAuthority(),
-                mMockContentProvider);
-        mConfigsCacheGenerationStore = new MemoryIntArray(2);
-        mConfigsCacheGenerationStore.set(0, 123);
-        mConfigsCacheGenerationStore.set(1, 456);
-        mSettingsCacheGenerationStore = new MemoryIntArray(2);
-        mSettingsCacheGenerationStore.set(0, 234);
-        mSettingsCacheGenerationStore.set(1, 567);
-        mConfigsStorage = new HashMap<>();
-        mSettingsStorage = new HashMap<>();
+        mCacheGenerationStore = new MemoryIntArray(1);
+        mStorage = new HashMap<>();
 
         // Stores keyValues for a given prefix and increments the generation. (Note that this
         // increments the generation no matter what, it doesn't pay attention to if anything
         // actually changed).
         when(mMockIContentProvider.call(any(), eq(Settings.Config.CONTENT_URI.getAuthority()),
-                eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class))).thenAnswer(
-                invocationOnMock -> {
+                eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+                any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
                     Bundle incomingBundle = invocationOnMock.getArgument(4);
                     HashMap<String, String> keyValues =
                             (HashMap<String, String>) incomingBundle.getSerializable(
-                                    Settings.CALL_METHOD_FLAGS_KEY, HashMap.class);
+                                    Settings.CALL_METHOD_FLAGS_KEY);
                     String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY);
-                    mConfigsStorage.put(prefix, keyValues);
-                    int currentGeneration;
-                    // Different prefixes have different generation codes
-                    if (prefix.equals(NAMESPACE + "/")) {
-                        currentGeneration = mConfigsCacheGenerationStore.get(0);
-                        mConfigsCacheGenerationStore.set(0, ++currentGeneration);
-                    } else if (prefix.equals(NAMESPACE2 + "/")) {
-                        currentGeneration = mConfigsCacheGenerationStore.get(1);
-                        mConfigsCacheGenerationStore.set(1, ++currentGeneration);
-                    }
+                    mStorage.put(prefix, keyValues);
+                    mCacheGenerationStore.set(0, ++mCurrentGeneration);
+
                     Bundle result = new Bundle();
                     result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN,
                             Settings.SET_ALL_RESULT_SUCCESS);
@@ -125,87 +102,32 @@
                 });
 
         // Returns the keyValues corresponding to a namespace, or an empty map if the namespace
-        // doesn't have anything stored for it. Returns the generation key if the map isn't empty
-        // and the caller asked for the generation key.
+        // doesn't have anything stored for it. Returns the generation key if the caller asked
+        // for one.
         when(mMockIContentProvider.call(any(), eq(Settings.Config.CONTENT_URI.getAuthority()),
-                eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class))).thenAnswer(
-                invocationOnMock -> {
+                eq(Settings.CALL_METHOD_LIST_CONFIG),
+                any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
                     Bundle incomingBundle = invocationOnMock.getArgument(4);
 
                     String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY);
 
-                    if (!mConfigsStorage.containsKey(prefix)) {
-                        mConfigsStorage.put(prefix, new HashMap<>());
+                    if (!mStorage.containsKey(prefix)) {
+                        mStorage.put(prefix, new HashMap<>());
                     }
-                    HashMap<String, String> keyValues = mConfigsStorage.get(prefix);
+                    HashMap<String, String> keyValues = mStorage.get(prefix);
 
                     Bundle bundle = new Bundle();
                     bundle.putSerializable(Settings.NameValueTable.VALUE, keyValues);
 
-                    if (!keyValues.isEmpty() && incomingBundle.containsKey(
-                            Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
-                        int index = prefix.equals(NAMESPACE + "/") ? 0 : 1;
+                    if (incomingBundle.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
                         bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
-                                mConfigsCacheGenerationStore);
-                        bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
+                                mCacheGenerationStore);
+                        bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, 0);
                         bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
-                                mConfigsCacheGenerationStore.get(index));
+                                mCacheGenerationStore.get(0));
                     }
                     return bundle;
                 });
-
-        // Stores value for a given setting's name and increments the generation. (Note that this
-        // increments the generation no matter what, it doesn't pay attention to if anything
-        // actually changed).
-        when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()),
-                eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class))).thenAnswer(
-                invocationOnMock -> {
-                    Bundle incomingBundle = invocationOnMock.getArgument(4);
-                    String key = invocationOnMock.getArgument(3);
-                    String value = incomingBundle.getString(Settings.NameValueTable.VALUE);
-                    mSettingsStorage.put(key, value);
-                    int currentGeneration;
-                    // Different settings have different generation codes
-                    if (key.equals(SETTING)) {
-                        currentGeneration = mSettingsCacheGenerationStore.get(0);
-                        mSettingsCacheGenerationStore.set(0, ++currentGeneration);
-                    } else if (key.equals(SETTING2)) {
-                        currentGeneration = mSettingsCacheGenerationStore.get(1);
-                        mSettingsCacheGenerationStore.set(1, ++currentGeneration);
-                    }
-                    return null;
-                });
-
-        // Returns the value corresponding to a setting, or null if the setting
-        // doesn't have a value stored for it. Returns the generation key if the value isn't null
-        // and the caller asked for the generation key.
-        when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer(
-                invocationOnMock -> {
-                    Bundle incomingBundle = invocationOnMock.getArgument(4);
-                    String key = invocationOnMock.getArgument(3);
-                    String value = mSettingsStorage.get(key);
-
-                    Bundle bundle = new Bundle();
-                    bundle.putSerializable(Settings.NameValueTable.VALUE, value);
-
-                    if (value != null && incomingBundle.containsKey(
-                            Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
-                        int index = key.equals(SETTING) ? 0 : 1;
-                        bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
-                                mSettingsCacheGenerationStore);
-                        bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
-                        bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
-                                mSettingsCacheGenerationStore.get(index));
-                    }
-                    return bundle;
-                });
-    }
-
-    @After
-    public void cleanUp() throws IOException {
-        mConfigsStorage.clear();
-        mSettingsStorage.clear();
     }
 
     @Test
@@ -213,13 +135,16 @@
         HashMap<String, String> keyValues = new HashMap<>();
         keyValues.put("a", "b");
         Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class));
+        verify(mMockIContentProvider).call(any(), any(),
+                eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+                any(), any(Bundle.class));
 
         Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
-                NAMESPACE, Collections.emptyList());
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class));
+                NAMESPACE,
+                Collections.emptyList());
+        verify(mMockIContentProvider).call(any(), any(),
+                eq(Settings.CALL_METHOD_LIST_CONFIG),
+                any(), any(Bundle.class));
         assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
 
         Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver,
@@ -231,12 +156,14 @@
         keyValues.put("a", "c");
         Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
         verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class));
+                eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+                any(), any(Bundle.class));
 
         Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
                 NAMESPACE, Collections.emptyList());
         verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class));
+                eq(Settings.CALL_METHOD_LIST_CONFIG),
+                any(), any(Bundle.class));
         assertThat(returnedValues2).containsExactlyEntriesIn(keyValues);
 
         Map<String, String> cachedKeyValues2 = Settings.Config.getStrings(mMockContentResolver,
@@ -250,31 +177,36 @@
         HashMap<String, String> keyValues = new HashMap<>();
         keyValues.put("a", "b");
         Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
-        verify(mMockIContentProvider).call(any(), any(), eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+        verify(mMockIContentProvider).call(any(), any(),
+                eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
                 any(), any(Bundle.class));
 
-        Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
-                NAMESPACE, Collections.emptyList());
-        verify(mMockIContentProvider).call(any(), any(), eq(Settings.CALL_METHOD_LIST_CONFIG),
-                any(), any(Bundle.class));
-        assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
-
         HashMap<String, String> keyValues2 = new HashMap<>();
         keyValues2.put("c", "d");
         keyValues2.put("e", "f");
         Settings.Config.setStrings(mMockContentResolver, NAMESPACE2, keyValues2);
         verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class));
+                eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+                any(), any(Bundle.class));
+
+        Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
+                NAMESPACE,
+                Collections.emptyList());
+        verify(mMockIContentProvider).call(any(), any(),
+                eq(Settings.CALL_METHOD_LIST_CONFIG),
+                any(), any(Bundle.class));
+        assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
 
         Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
-                NAMESPACE2, Collections.emptyList());
+                NAMESPACE2,
+                Collections.emptyList());
         verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class));
+                eq(Settings.CALL_METHOD_LIST_CONFIG),
+                any(), any(Bundle.class));
         assertThat(returnedValues2).containsExactlyEntriesIn(keyValues2);
 
         Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver,
                 NAMESPACE, Collections.emptyList());
-        // Modifying the second namespace doesn't affect the cache of the first namespace
         verifyNoMoreInteractions(mMockIContentProvider);
         assertThat(cachedKeyValues).containsExactlyEntriesIn(keyValues);
 
@@ -287,90 +219,17 @@
     @Test
     public void testCaching_emptyNamespace() throws Exception {
         Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
-                NAMESPACE, Collections.emptyList());
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class));
+                NAMESPACE,
+                Collections.emptyList());
+        verify(mMockIContentProvider).call(any(), any(),
+                eq(Settings.CALL_METHOD_LIST_CONFIG),
+                any(), any(Bundle.class));
         assertThat(returnedValues).isEmpty();
 
         Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver,
                 NAMESPACE, Collections.emptyList());
-        // Empty list won't be cached
-        verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class));
+        verifyNoMoreInteractions(mMockIContentProvider);
         assertThat(cachedKeyValues).isEmpty();
     }
 
-    @Test
-    public void testCaching_singleSetting() throws Exception {
-        Settings.Secure.putString(mMockContentResolver, SETTING, "a");
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class));
-
-        String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue).isEqualTo("a");
-
-        String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verifyNoMoreInteractions(mMockIContentProvider);
-        assertThat(cachedValue).isEqualTo("a");
-
-        // Modify the value to invalidate the cache.
-        Settings.Secure.putString(mMockContentResolver, SETTING, "b");
-        verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class));
-
-        String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue2).isEqualTo("b");
-
-        String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verifyNoMoreInteractions(mMockIContentProvider);
-        assertThat(cachedValue2).isEqualTo("b");
-    }
-
-    @Test
-    public void testCaching_multipleSettings() throws Exception {
-        Settings.Secure.putString(mMockContentResolver, SETTING, "a");
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class));
-
-        String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue).isEqualTo("a");
-
-        Settings.Secure.putString(mMockContentResolver, SETTING2, "b");
-        verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class));
-
-        String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
-        verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue2).isEqualTo("b");
-
-        String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        // Modifying the second setting doesn't affect the cache of the first setting
-        verifyNoMoreInteractions(mMockIContentProvider);
-        assertThat(cachedValue).isEqualTo("a");
-
-        String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2);
-        verifyNoMoreInteractions(mMockIContentProvider);
-        assertThat(cachedValue2).isEqualTo("b");
-    }
-
-    @Test
-    public void testCaching_nullSetting() throws Exception {
-        String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        verify(mMockIContentProvider, times(1)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(returnedValue).isNull();
-
-        String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
-        // Empty list won't be cached
-        verify(mMockIContentProvider, times(2)).call(any(), any(),
-                eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class));
-        assertThat(cachedValue).isNull();
-    }
 }
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index 32078ca..f723f15 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -47,14 +47,14 @@
     private static final int ACTION = KeyEvent.ACTION_DOWN;
     private static final int ANOTHER_ACTION = KeyEvent.ACTION_UP;
     private static final int KEYCODE = KeyEvent.KEYCODE_0;
-    private static final int REPEAT = 0;
-    private static final int ANOTHER_REPEAT = 42;
-    private static final int METASTATE = 0;
-    private static final int DEVICE_ID = 0;
-    private static final int SCAN_CODE = 0;
-    private static final int FLAGS = 0;
+    private static final int REPEAT = 4;
+    private static final int ANOTHER_REPEAT = 8;
+    private static final int METASTATE = 15;
+    private static final int DEVICE_ID = 16;
+    private static final int SCAN_CODE = 23;
+    private static final int FLAGS = 42;
     private static final int SOURCE = InputDevice.SOURCE_KEYBOARD;
-    private static final String CHARACTERS = null;
+    private static final String CHARACTERS = "CHARACTERS, Y U NO @NONNULL?";
 
     private static final int ID_SOURCE_MASK = 0x3 << 30;
 
@@ -178,7 +178,7 @@
                 DEVICE_ID, SCAN_CODE, FLAGS, SOURCE);
 
         assertKeyEventFields(key, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
-                DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, CHARACTERS);
+                DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, /* characters= */ null);
     }
 
     @Test
@@ -187,7 +187,8 @@
                 DEVICE_ID, SCAN_CODE, FLAGS);
 
         assertKeyEventFields(key, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
-                DEVICE_ID, SCAN_CODE, FLAGS, /* source= */ 0, INVALID_DISPLAY, CHARACTERS);
+                DEVICE_ID, SCAN_CODE, FLAGS, /* source= */ 0, INVALID_DISPLAY,
+                /* characters= */ null);
     }
 
     @Test
@@ -197,7 +198,7 @@
 
         assertKeyEventFields(key, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
                 DEVICE_ID, SCAN_CODE, /* flags= */ 0, /* source= */ 0, INVALID_DISPLAY,
-                CHARACTERS);
+                /* characters= */ null);
     }
 
     @Test
@@ -206,7 +207,7 @@
 
         assertKeyEventFields(key, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
                 /* deviceId= */ KeyCharacterMap.VIRTUAL_KEYBOARD, /* scanCode= */ 0, /* flags= */ 0,
-                /* source= */ 0, INVALID_DISPLAY, CHARACTERS);
+                /* source= */ 0, INVALID_DISPLAY, /* characters= */ null);
     }
 
     @Test
@@ -215,7 +216,8 @@
 
         assertKeyEventFields(key, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
                 /* metaState= */ 0, /* deviceId= */ KeyCharacterMap.VIRTUAL_KEYBOARD,
-                /* scanCode= */ 0, /* flags= */ 0, /* source= */ 0, INVALID_DISPLAY, CHARACTERS);
+                /* scanCode= */ 0, /* flags= */ 0, /* source= */ 0, INVALID_DISPLAY,
+                /* characters= */ null);
     }
 
     @Test
@@ -233,8 +235,8 @@
 
         assertKeyEventFields(key, /* downTime= */ 0, /* eventTime= */ 0, ACTION, KEYCODE,
                 /* repeat= */ 0, /* metaState= */ 0,
-                /* deviceId= */ KeyCharacterMap.VIRTUAL_KEYBOARD, /* scanCode= */ 0, FLAGS,
-                /* source= */ 0, INVALID_DISPLAY, CHARACTERS);
+                /* deviceId= */ KeyCharacterMap.VIRTUAL_KEYBOARD, /* scanCode= */ 0, /* flags= */ 0,
+                /* source= */ 0, INVALID_DISPLAY, /* characters= */ null);
     }
 
     @Test
@@ -244,7 +246,7 @@
 
         assertKeyEventFields(key2, DOWN_TIME, EVENT_TIME, ANOTHER_ACTION, KEYCODE, REPEAT,
                 METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, InputDevice.SOURCE_KEYBOARD,
-                INVALID_DISPLAY, CHARACTERS);
+                INVALID_DISPLAY, /* characters= */ null);
         expect.withMessage("id (key1=%s, key2=%s", key1, key2).that(key2.getId())
                 .isNotEqualTo(key1.getId());
     }
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
index 0c7550e..777246b 100644
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
@@ -33,6 +33,8 @@
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.Icon;
 import android.view.ContextMenu;
 import android.view.MenuItem;
@@ -167,4 +169,75 @@
         assertThat(idCaptor.getValue()).isEqualTo(TextView.ID_ASSIST);
         assertThat(titleCaptor.getValue().toString()).isEqualTo(ACTION_TITLE);
     }
+
+    @UiThreadTest
+    @Test
+    public void testAdjustIconSpaces() {
+        GradientDrawable gd = new GradientDrawable();
+        gd.setSize(128, 256);
+
+        // Setup mocks
+        ContextMenu menu = mock(ContextMenu.class);
+
+        MenuItem mockIconMenu = newMockMenuItem();
+        when(mockIconMenu.getIcon()).thenReturn(gd);
+
+        MenuItem mockNoIconMenu = newMockMenuItem();
+        when(mockNoIconMenu.getIcon()).thenReturn(null);
+
+        MenuItem mockNoIconMenu2 = newMockMenuItem();
+        when(mockNoIconMenu2.getIcon()).thenReturn(null);
+
+        when(menu.size()).thenReturn(3);
+        when(menu.getItem(0)).thenReturn(mockIconMenu);
+        when(menu.getItem(1)).thenReturn(mockNoIconMenu);
+        when(menu.getItem(2)).thenReturn(mockNoIconMenu2);
+
+
+        // Execute the test method
+        EditText et = mActivity.findViewById(R.id.editText);
+        Editor editor = et.getEditorForTesting();
+        editor.adjustIconSpacing(menu);
+
+        // Verify
+        ArgumentCaptor<Drawable> drawableCaptor = ArgumentCaptor.forClass(Drawable.class);
+        verify(mockNoIconMenu).setIcon(drawableCaptor.capture());
+
+        Drawable paddingDrawable = drawableCaptor.getValue();
+        assertThat(paddingDrawable).isNotNull();
+        assertThat(paddingDrawable.getIntrinsicWidth()).isEqualTo(128);
+        assertThat(paddingDrawable.getIntrinsicHeight()).isEqualTo(256);
+
+        ArgumentCaptor<Drawable> drawableCaptor2 = ArgumentCaptor.forClass(Drawable.class);
+        verify(mockNoIconMenu2).setIcon(drawableCaptor2.capture());
+
+        Drawable paddingDrawable2 = drawableCaptor2.getValue();
+        assertThat(paddingDrawable2).isSameInstanceAs(paddingDrawable);
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAdjustIconSpacesNoIconCase() {
+        // Setup mocks
+        ContextMenu menu = mock(ContextMenu.class);
+
+        MenuItem mockNoIconMenu = newMockMenuItem();
+        when(mockNoIconMenu.getIcon()).thenReturn(null);
+
+        MenuItem mockNoIconMenu2 = newMockMenuItem();
+        when(mockNoIconMenu2.getIcon()).thenReturn(null);
+
+        when(menu.size()).thenReturn(2);
+        when(menu.getItem(0)).thenReturn(mockNoIconMenu);
+        when(menu.getItem(1)).thenReturn(mockNoIconMenu2);
+
+        // Execute the test method
+        EditText et = mActivity.findViewById(R.id.editText);
+        Editor editor = et.getEditorForTesting();
+        editor.adjustIconSpacing(menu);
+
+        // Verify
+        verify(mockNoIconMenu, times(0)).setIcon(any());
+        verify(mockNoIconMenu2, times(0)).setIcon(any());
+    }
 }
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTest.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
index 9ccf3b3..3b8b8c7 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTest.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -117,6 +117,31 @@
     }
 
     @Test
+    public void testGetReportedHdrTypes_returns_mode_specific_hdr_types() {
+        setDisplayInfoPortrait(mDisplayInfo);
+        float[] alternativeRefreshRates = new float[0];
+        int[] hdrTypesWithDv = new int[] {1, 2, 3, 4};
+        Display.Mode modeWithDv = new Display.Mode(/* modeId= */ 0, 0, 0, 0f,
+                alternativeRefreshRates, hdrTypesWithDv);
+
+        int[] hdrTypesWithoutDv = new int[]{2, 3, 4};
+        Display.Mode modeWithoutDv = new Display.Mode(/* modeId= */ 1, 0, 0, 0f,
+                alternativeRefreshRates, hdrTypesWithoutDv);
+
+        mDisplayInfo.supportedModes = new Display.Mode[] {modeWithoutDv, modeWithDv};
+        mDisplayInfo.hdrCapabilities = new Display.HdrCapabilities(hdrTypesWithDv, 0, 0, 0);
+
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+
+        mDisplayInfo.modeId = 0;
+        assertArrayEquals(hdrTypesWithDv, display.getReportedHdrTypes());
+
+        mDisplayInfo.modeId = 1;
+        assertArrayEquals(hdrTypesWithoutDv, display.getReportedHdrTypes());
+    }
+
+    @Test
     public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
         setDisplayInfoPortrait(mDisplayInfo);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 16fdffa..f3318f4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -122,6 +122,7 @@
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
         <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
         <permission name="android.permission.BIND_IMS_SERVICE"/>
+        <permission name="android.permission.BIND_SATELLITE_SERVICE"/>
         <permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
         <permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
         <permission name="android.permission.CALL_PRIVILEGED"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index acf17e6..b3fff1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -29,8 +29,9 @@
 import android.database.ContentObserver;
 import android.hardware.input.InputManager;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -39,7 +40,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.IRemoteAnimationRunner;
-import android.view.IWindowFocusObserver;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -121,23 +121,22 @@
     private IOnBackInvokedCallback mActiveCallback;
 
     @VisibleForTesting
-    final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
-        @Override
-        public void focusGained(IBinder inputToken) { }
-        @Override
-        public void focusLost(IBinder inputToken) {
-            mShellExecutor.execute(() -> {
-                if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
-                    // If an uninterruptible animation is already in progress, we should ignore
-                    // this due to it may cause focus lost. (alpha = 0)
-                    return;
+    final RemoteCallback mNavigationObserver = new RemoteCallback(
+            new RemoteCallback.OnResultListener() {
+                @Override
+                public void onResult(@Nullable Bundle result) {
+                    mShellExecutor.execute(() -> {
+                        if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
+                            // If an uninterruptible animation is already in progress, we should
+                            // ignore this due to it may cause focus lost. (alpha = 0)
+                            return;
+                        }
+                        ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
+                        setTriggerBack(false);
+                        onGestureFinished(false);
+                    });
                 }
-                ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Target window lost focus.");
-                setTriggerBack(false);
-                onGestureFinished(false);
             });
-        }
-    };
 
     private final BackAnimationBackground mAnimationBackground;
 
@@ -351,7 +350,7 @@
 
         try {
             mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
-                    mFocusObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
+                    mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
             onBackNavigationInfoReceived(mBackNavigationInfo);
         } catch (RemoteException remoteException) {
             Log.e(TAG, "Failed to initAnimation", remoteException);
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 f616e6f..8a18271 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
@@ -1091,7 +1091,7 @@
             // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
             // because DividerView won't receive onImeVisibilityChanged callback after it being
             // re-inflated.
-            mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus,
+            mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus || isFloating,
                     "onImeStartPositioning");
 
             return needOffset ? IME_ANIMATION_NO_ALPHA : 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index fce0138..73a7403 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -85,7 +85,7 @@
     fun showDesktopApps() {
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps")
         val wct = WindowContainerTransaction()
-        bringDesktopAppsToFront(wct, force = true)
+        bringDesktopAppsToFront(wct)
 
         // Execute transaction if there are pending operations
         if (!wct.isEmpty) {
@@ -156,19 +156,9 @@
             ?: WINDOWING_MODE_UNDEFINED
     }
 
-    private fun bringDesktopAppsToFront(wct: WindowContainerTransaction, force: Boolean = false) {
-        val activeTasks = desktopModeTaskRepository.getActiveTasks()
-
-        // Skip if all tasks are already visible
-        if (!force && activeTasks.all(desktopModeTaskRepository::isVisibleTask)) {
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "bringDesktopAppsToFront: active tasks are already in front, skipping."
-            )
-            return
-        }
-
+    private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) {
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront")
+        val activeTasks = desktopModeTaskRepository.getActiveTasks()
 
         // First move home to front and then other tasks on top of it
         moveHomeTaskToFront(wct)
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 26f47fc..d094c22 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
@@ -108,6 +108,10 @@
         }
         if (!createdWindowDecor) {
             mSyncQueue.runInSync(t -> {
+                if (!leash.isValid()) {
+                    // Task vanished before sync completion
+                    return;
+                }
                 // Reset several properties back to fullscreen (PiP, for example, leaves all these
                 // properties in a bad state).
                 t.setWindowCrop(leash, null);
@@ -136,6 +140,10 @@
         final Point positionInParent = state.mTaskInfo.positionInParent;
         if (!oldPositionInParent.equals(state.mTaskInfo.positionInParent)) {
             mSyncQueue.runInSync(t -> {
+                if (!state.mLeash.isValid()) {
+                    // Task vanished before sync completion
+                    return;
+                }
                 t.setPosition(state.mLeash, positionInParent.x, positionInParent.y);
             });
         }
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 2981f5e..9224b3c 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
@@ -174,7 +174,7 @@
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final TaskPositioner taskPositioner =
-                new TaskPositioner(mTaskOrganizer, windowDecoration);
+                new TaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController);
         final CaptionTouchEventListener touchEventListener =
                 new CaptionTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 75bc985..766c4cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -558,7 +558,8 @@
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final TaskPositioner taskPositioner =
-                new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
+                new TaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController,
+                        mDragStartListener);
         final DesktopModeTouchEventListener touchEventListener =
                 new DesktopModeTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index d3f9227..a3d364a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -19,9 +19,11 @@
 import android.annotation.IntDef;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.util.DisplayMetrics;
 import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
 
 class TaskPositioner implements DragPositioningCallback {
 
@@ -35,6 +37,7 @@
     static final int CTRL_TYPE_BOTTOM = 8;
 
     private final ShellTaskOrganizer mTaskOrganizer;
+    private final DisplayController mDisplayController;
     private final WindowDecoration mWindowDecoration;
 
     private final Rect mTaskBoundsAtDragStart = new Rect();
@@ -45,14 +48,16 @@
     private int mCtrlType;
     private DragStartListener mDragStartListener;
 
-    TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration) {
-        this(taskOrganizer, windowDecoration, dragStartListener -> {});
+    TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
+            DisplayController displayController) {
+        this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {});
     }
 
     TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
-            DragStartListener dragStartListener) {
+            DisplayController displayController, DragStartListener dragStartListener) {
         mTaskOrganizer = taskOrganizer;
         mWindowDecoration = windowDecoration;
+        mDisplayController = displayController;
         mDragStartListener = dragStartListener;
     }
 
@@ -128,15 +133,44 @@
             mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
         }
 
+        // If width or height are negative or less than the minimum width or height, revert the
+        // respective bounds to use previous bound dimensions.
+        if (mRepositionTaskBounds.width() < getMinWidth()) {
+            mRepositionTaskBounds.right = oldRight;
+            mRepositionTaskBounds.left = oldLeft;
+        }
+        if (mRepositionTaskBounds.height() < getMinHeight()) {
+            mRepositionTaskBounds.top = oldTop;
+            mRepositionTaskBounds.bottom = oldBottom;
+        }
+        // If there are no changes to the bounds after checking new bounds against minimum width
+        // and height, do not set bounds and return false
         if (oldLeft == mRepositionTaskBounds.left && oldTop == mRepositionTaskBounds.top
                 && oldRight == mRepositionTaskBounds.right
                 && oldBottom == mRepositionTaskBounds.bottom) {
             return false;
         }
+
         wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
         return true;
     }
 
+    private float getMinWidth() {
+        return mWindowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize()
+                : mWindowDecoration.mTaskInfo.minWidth;
+    }
+
+    private float getMinHeight() {
+        return mWindowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinSize()
+                : mWindowDecoration.mTaskInfo.minHeight;
+    }
+
+    private float getDefaultMinSize() {
+        float density =  mDisplayController.getDisplayLayout(mWindowDecoration.mTaskInfo.displayId)
+                .densityDpi() * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        return mWindowDecoration.mTaskInfo.defaultMinSize * density;
+    }
+
     interface DragStartListener {
         /**
          * Inform the implementing class that a drag resize has started
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 8a5b490..5a4a44f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
@@ -40,7 +39,6 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.provider.Settings;
@@ -341,8 +339,7 @@
         mController.setTriggerBack(true);   // Fake trigger back
 
         // In case the focus has been changed.
-        IBinder token = mock(IBinder.class);
-        mController.mFocusObserver.focusLost(token);
+        mController.mNavigationObserver.sendResult(null);
         mShellExecutor.flushAll();
         verify(mAnimatorCallback).onBackCancelled();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index f185a8a..8f66f4e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -8,6 +8,8 @@
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
 import androidx.test.filters.SmallTest
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
@@ -45,6 +47,11 @@
     @Mock
     private lateinit var taskBinder: IBinder
 
+    @Mock
+    private lateinit var mockDisplayController: DisplayController
+    @Mock
+    private lateinit var mockDisplayLayout: DisplayLayout
+
     private lateinit var taskPositioner: TaskPositioner
 
     @Before
@@ -54,12 +61,21 @@
         taskPositioner = TaskPositioner(
                 mockShellTaskOrganizer,
                 mockWindowDecoration,
+                mockDisplayController,
                 mockDragStartListener
         )
+
         `when`(taskToken.asBinder()).thenReturn(taskBinder)
+        `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
+        `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+
         mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
             taskId = TASK_ID
             token = taskToken
+            minWidth = MIN_WIDTH
+            minHeight = MIN_HEIGHT
+            defaultMinSize = DEFAULT_MIN
+            displayId = DISPLAY_ID
             configuration.windowConfiguration.bounds = STARTING_BOUNDS
         }
     }
@@ -209,8 +225,239 @@
         })
     }
 
+    @Test
+    fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenLessThanMin() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Resize to width of 95px and height of 5px with min width of 10px
+        val newX = STARTING_BOUNDS.right.toFloat() - 5
+        val newY = STARTING_BOUNDS.top.toFloat() + 95
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
+                                != 0) && change.configuration.windowConfiguration.bounds.top ==
+                        STARTING_BOUNDS.top &&
+                        change.configuration.windowConfiguration.bounds.bottom ==
+                        STARTING_BOUNDS.bottom
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenLessThanMin() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Resize to height of 95px and width of 5px with min width of 10px
+        val newX = STARTING_BOUNDS.right.toFloat() - 95
+        val newY = STARTING_BOUNDS.top.toFloat() + 5
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
+                                != 0) && change.configuration.windowConfiguration.bounds.right ==
+                        STARTING_BOUNDS.right &&
+                        change.configuration.windowConfiguration.bounds.left ==
+                        STARTING_BOUNDS.left
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenNegative() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Resize to height of -5px and width of 95px
+        val newX = STARTING_BOUNDS.right.toFloat() - 5
+        val newY = STARTING_BOUNDS.top.toFloat() + 105
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
+                                != 0) && change.configuration.windowConfiguration.bounds.top ==
+                        STARTING_BOUNDS.top &&
+                        change.configuration.windowConfiguration.bounds.bottom ==
+                        STARTING_BOUNDS.bottom
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenNegative() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Resize to width of -5px and height of 95px
+        val newX = STARTING_BOUNDS.right.toFloat() - 105
+        val newY = STARTING_BOUNDS.top.toFloat() + 5
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS)
+                                != 0) && change.configuration.windowConfiguration.bounds.right ==
+                        STARTING_BOUNDS.right &&
+                        change.configuration.windowConfiguration.bounds.left ==
+                        STARTING_BOUNDS.left
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_setBoundsRunsWhenResizeBoundsValid() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Shrink to height 20px and width 20px with both min height/width equal to 10px
+        val newX = STARTING_BOUNDS.right.toFloat() - 80
+        val newY = STARTING_BOUNDS.top.toFloat() + 80
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_setBoundsDoesNotRunWithNegativeHeightAndWidth() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Shrink to height 5px and width 5px with both min height/width equal to 10px
+        val newX = STARTING_BOUNDS.right.toFloat() - 95
+        val newY = STARTING_BOUNDS.top.toFloat() + 95
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_useDefaultMinWhenMinWidthInvalid() {
+        mockWindowDecoration.mTaskInfo.minWidth = -1
+
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Shrink to width and height of 3px with invalid minWidth = -1 and defaultMinSize = 5px
+        val newX = STARTING_BOUNDS.right.toFloat() - 97
+        val newY = STARTING_BOUNDS.top.toFloat() + 97
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_resize_useMinWidthWhenValid() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        // Shrink to width and height of 7px with valid minWidth = 10px and defaultMinSize = 5px
+        val newX = STARTING_BOUNDS.right.toFloat() - 93
+        val newY = STARTING_BOUNDS.top.toFloat() + 93
+        taskPositioner.onDragPositioningMove(
+                newX,
+                newY
+        )
+
+        taskPositioner.onDragPositioningEnd(newX, newY)
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
     companion object {
         private const val TASK_ID = 5
+        private const val MIN_WIDTH = 10
+        private const val MIN_HEIGHT = 10
+        private const val DENSITY_DPI = 20
+        private const val DEFAULT_MIN = 40
+        private const val DISPLAY_ID = 1
         private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
     }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 0b08681..faa7f7f 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -3049,7 +3049,13 @@
 
 
         /**
-         * Gets the {@link LogSessionId}.
+         * Sets the {@link LogSessionId}.
+         *
+         * <p>The implementation of this method varies by DRM provider; Please refer
+         * to your DRM provider documentation for more details on this method.
+         *
+         * @throws UnsupportedOperationException when the vendor plugin does not
+         * implement this method
          */
         public void setLogSessionId(@NonNull LogSessionId logSessionId) {
             Objects.requireNonNull(logSessionId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index afc2bb1..6eed483 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -190,9 +190,7 @@
                 break;
             }
             case DO_SEND_TIME_SHIFT_MODE: {
-                SomeArgs args = (SomeArgs) msg.obj;
-                mSessionImpl.sendTimeShiftMode(args.argi1);
-                args.recycle();
+                mSessionImpl.sendTimeShiftMode((Integer) msg.obj);
                 break;
             }
             case DO_SEND_AVAILABLE_SPEEDS: {
@@ -447,7 +445,7 @@
 
     @Override
     public void sendTimeShiftMode(int mode) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SEND_TIME_SHIFT_MODE, mode));
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SEND_TIME_SHIFT_MODE, mode));
     }
 
     @Override
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
index b36cb5c..dfc8aa0 100644
--- a/packages/CredentialManager/AndroidManifest.xml
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -41,6 +41,15 @@
         android:excludeFromRecents="true"
         android:theme="@style/Theme.CredentialSelector">
     </activity>
+
+    <receiver
+        android:name=".CredentialProviderReceiver"
+        android:exported="true"
+        android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR">
+        <intent-filter>
+            <action android:name="android.credentials.ui.action.CREDMAN_ENABLED_PROVIDERS_UPDATED"/>
+        </intent-filter>
+    </receiver>
   </application>
 
 </manifest>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 0d25bec..30b97bf 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -73,7 +73,7 @@
         requestInfo = intent.extras?.getParcelable(
             RequestInfo.EXTRA_REQUEST_INFO,
             RequestInfo::class.java
-        ) ?: testCreatePasskeyRequestInfo()
+        ) ?: testCreatePasswordRequestInfo()
 
         providerEnabledList = when (requestInfo.type) {
             RequestInfo.TYPE_CREATE ->
@@ -402,30 +402,26 @@
         )
         val credentialData = request.credentialData
         return RequestInfo.newCreateRequestInfo(
-            Binder(),
-            CreateCredentialRequest(
-                "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
-                credentialData,
-                /*candidateQueryData=*/ Bundle(),
-                /*isSystemProviderRequired=*/ false,
-                /*alwaysSendAppInfoToProvider=*/ true
-            ),
-            "com.google.android.youtube"
+                Binder(),
+                CreateCredentialRequest.Builder(credentialData, Bundle())
+                        .setType("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL")
+                        .setIsSystemProviderRequired(false)
+                        .setAlwaysSendAppInfoToProvider(true)
+                        .build(),
+                "com.google.android.youtube"
         )
     }
 
     private fun testCreatePasswordRequestInfo(): RequestInfo {
         val request = CreatePasswordRequest("beckett-bakert@gmail.com", "password123")
         return RequestInfo.newCreateRequestInfo(
-            Binder(),
-            CreateCredentialRequest(
-                TYPE_PASSWORD_CREDENTIAL,
-                request.credentialData,
-                request.candidateQueryData,
-                /*isSystemProviderRequired=*/ false,
-                /*alwaysSendAppInfoToProvider=*/ true
-            ),
-            "com.google.android.youtube"
+                Binder(),
+                CreateCredentialRequest.Builder(request.credentialData, request.candidateQueryData)
+                        .setType(TYPE_PASSWORD_CREDENTIAL)
+                        .setIsSystemProviderRequired(false)
+                        .setAlwaysSendAppInfoToProvider(true)
+                        .build(),
+                "com.google.android.youtube"
         )
     }
 
@@ -437,15 +433,13 @@
             displayInfo.toBundle()
         )
         return RequestInfo.newCreateRequestInfo(
-            Binder(),
-            CreateCredentialRequest(
-                "other-sign-ins",
-                data,
-                /*candidateQueryData=*/ Bundle(),
-                /*isSystemProviderRequired=*/ false,
-                /*alwaysSendAppInfoToProvider=*/ true
-            ),
-            "com.google.android.youtube"
+                Binder(),
+                CreateCredentialRequest.Builder(data, Bundle())
+                        .setType("other-sign-ins")
+                        .setIsSystemProviderRequired(false)
+                        .setAlwaysSendAppInfoToProvider(true)
+                        .build(),
+                "com.google.android.youtube"
         )
     }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt
new file mode 100644
index 0000000..ee8cffe
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import com.android.credentialmanager.common.Constants
+
+
+class CredentialProviderReceiver : BroadcastReceiver() {
+
+    override fun onReceive(context: Context?, intent: Intent?) {
+        Log.d(Constants.LOG_TAG, "Received intent in CredentialProviderReceiver")
+
+        val sharedPreferences = context?.getSharedPreferences(context?.packageName,
+                Context.MODE_PRIVATE)
+        sharedPreferences?.edit()?.remove(UserConfigRepo.DEFAULT_PROVIDER)?.commit()
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
index d1dceb3..5396de0 100644
--- a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
+++ b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
@@ -17,6 +17,7 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.settingslib.spaprivileged">
-<uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
 </manifest>
 
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
index 171903f..c609004 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
@@ -16,11 +16,14 @@
 
 package com.android.settingslib.spaprivileged.model.app
 
+import android.app.AppOpsManager;
 import android.app.AppOpsManager.MODE_ALLOWED
 import android.app.AppOpsManager.MODE_ERRORED
 import android.app.AppOpsManager.Mode
 import android.content.Context
 import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.map
@@ -44,17 +47,25 @@
     private val setModeByUid: Boolean = false,
 ) : IAppOpsController {
     private val appOpsManager = context.appOpsManager
+    private val packageManager = context.packageManager
 
     override val mode: LiveData<Int>
         get() = _mode
 
     override fun setAllowed(allowed: Boolean) {
         val mode = if (allowed) MODE_ALLOWED else modeForNotAllowed
+
         if (setModeByUid) {
             appOpsManager.setUidMode(op, app.uid, mode)
         } else {
             appOpsManager.setMode(op, app.uid, app.packageName, mode)
         }
+
+        val permission = AppOpsManager.opToPermission(op)
+        packageManager.updatePermissionFlags(permission, app.packageName,
+                PackageManager.FLAG_PERMISSION_USER_SET, PackageManager.FLAG_PERMISSION_USER_SET,
+                UserHandle.getUserHandleForUid(app.uid))
+
         _mode.postValue(mode)
     }
 
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt
index fd2ceb7..23270c1 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt
@@ -22,6 +22,7 @@
 import android.app.AppOpsManager.MODE_IGNORED
 import android.content.Context
 import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settingslib.spaprivileged.framework.common.appOpsManager
@@ -31,6 +32,10 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.Spy
@@ -45,9 +50,13 @@
 
     @Mock private lateinit var appOpsManager: AppOpsManager
 
+    @Mock private lateinit var packageManager: PackageManager
+
     @Before
     fun setUp() {
         whenever(context.appOpsManager).thenReturn(appOpsManager)
+        doNothing().`when`(packageManager)
+                .updatePermissionFlags(anyString(), anyString(), anyInt(), anyInt(), any())
     }
 
     @Test
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc012/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc012/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc012/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc590/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc590/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc590/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc591/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc591/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc591/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc592/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc592/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc592/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc593/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc593/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc593/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc594/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc594/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc594/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc595/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc595/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc595/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc596/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc596/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc596/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc597/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc597/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc597/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc598/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc598/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc598/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc599/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc599/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc599/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc310-mnc890/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc310-mnc890/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc310-mnc890/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc270/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc270/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc270/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc280/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc280/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc280/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc281/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc281/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc281/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc282/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc282/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc282/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc283/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc283/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc283/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc284/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc284/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc284/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc285/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc285/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc285/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc286/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc286/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc286/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc287/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc287/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc287/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc288/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc288/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc288/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc289/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc289/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc289/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc481/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc481/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc481/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc482/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc482/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc482/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc483/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc483/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc483/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc484/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc484/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc484/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc485/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc485/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc485/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc486/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc486/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc486/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc487/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc487/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc487/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc488/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc488/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc488/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable-mcc311-mnc489/ic_5g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable-mcc311-mnc489/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..c1103fe
--- /dev/null
+++ b/packages/SettingsLib/res/drawable-mcc311-mnc489/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc012/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc012/strings.xml
new file mode 100644
index 0000000..aa504ef
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc012/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
+
diff --git a/packages/SettingsLib/res/values-mcc310-mnc590/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc590/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc590/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc591/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc591/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc591/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc592/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc592/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc592/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc593/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc593/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc593/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc594/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc594/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc594/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc595/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc595/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc595/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc596/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc596/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc596/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc597/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc597/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc597/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc598/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc598/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc598/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc599/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc599/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc599/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc310-mnc890/strings.xml b/packages/SettingsLib/res/values-mcc310-mnc890/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc310-mnc890/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc270/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc270/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc270/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc280/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc280/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc280/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc281/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc281/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc281/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc282/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc282/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc282/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc283/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc283/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc283/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc284/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc284/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc284/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc285/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc285/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc285/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc286/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc286/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc286/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc287/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc287/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc287/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc288/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc288/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc288/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc289/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc289/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc289/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc481/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc481/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc481/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc482/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc482/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc482/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc483/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc483/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc483/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc484/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc484/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc484/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc485/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc485/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc485/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc486/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc486/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc486/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc487/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc487/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc487/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc488/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc488/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc488/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-mcc311-mnc489/strings.xml b/packages/SettingsLib/res/values-mcc311-mnc489/strings.xml
new file mode 100644
index 0000000..4cd04d0
--- /dev/null
+++ b/packages/SettingsLib/res/values-mcc311-mnc489/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 346462d..1ac20471 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -48,7 +48,6 @@
         "test/**/*.java",
         "src/android/provider/settings/backup/*",
         "src/android/provider/settings/validators/*",
-        "src/com/android/providers/settings/GenerationRegistry.java",
         "src/com/android/providers/settings/SettingsBackupAgent.java",
         "src/com/android/providers/settings/SettingsState.java",
         "src/com/android/providers/settings/SettingsHelper.java",
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 7f3b0ff..5617331 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -17,19 +17,19 @@
 package com.android.providers.settings;
 
 import android.os.Bundle;
+import android.os.UserManager;
 import android.provider.Settings;
-import android.util.ArrayMap;
 import android.util.MemoryIntArray;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
 
 /**
  * This class tracks changes for config/global/secure/system tables
- * on a per user basis and updates shared memory regions which
+ * on a per user basis and updates a shared memory region which
  * client processes can read to determine if their local caches are
  * stale.
  */
@@ -40,187 +40,138 @@
 
     private final Object mLock;
 
-    // Key -> backingStore mapping
     @GuardedBy("mLock")
-    private final ArrayMap<Integer, MemoryIntArray> mKeyToBackingStoreMap = new ArrayMap();
-
-    // Key -> (String->Index map) mapping
-    @GuardedBy("mLock")
-    private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>();
-
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    // Maximum number of backing stores allowed
-    static final int NUM_MAX_BACKING_STORE = 8;
+    private final SparseIntArray mKeyToIndexMap = new SparseIntArray();
 
     @GuardedBy("mLock")
-    private int mNumBackingStore = 0;
-
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    // Maximum size of an individual backing store
-    static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize();
+    private MemoryIntArray mBackingStore;
 
     public GenerationRegistry(Object lock) {
         mLock = lock;
     }
 
-    /**
-     *  Increment the generation number if the setting is already cached in the backing stores.
-     *  Otherwise, do nothing.
-     */
-    public void incrementGeneration(int key, String name) {
-        final boolean isConfig =
-                (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG);
-        // Only store the prefix if the mutated setting is a config
-        final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name;
+    public void incrementGeneration(int key) {
         synchronized (mLock) {
-            final MemoryIntArray backingStore = getBackingStoreLocked(key,
-                    /* createIfNotExist= */ false);
-            if (backingStore == null) {
-                return;
-            }
-            try {
-                final int index = getKeyIndexLocked(key, indexMapKey, mKeyToIndexMapMap,
-                        backingStore, /* createIfNotExist= */ false);
-                if (index < 0) {
-                    return;
+            MemoryIntArray backingStore = getBackingStoreLocked();
+            if (backingStore != null) {
+                try {
+                    final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore);
+                    if (index >= 0) {
+                        final int generation = backingStore.get(index) + 1;
+                        backingStore.set(index, generation);
+                    }
+                } catch (IOException e) {
+                    Slog.e(LOG_TAG, "Error updating generation id", e);
+                    destroyBackingStore();
                 }
-                final int generation = backingStore.get(index) + 1;
-                backingStore.set(index, generation);
-                if (DEBUG) {
-                    Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey
-                            + " key:" + SettingsState.keyToString(key)
-                            + " at index:" + index);
-                }
-            } catch (IOException e) {
-                Slog.e(LOG_TAG, "Error updating generation id", e);
-                destroyBackingStoreLocked(key);
             }
         }
     }
 
-    /**
-     *  Return the backing store's reference, the index and the current generation number
-     *  of a cached setting. If it was not in the backing store, first create the entry in it before
-     *  returning the result.
-     */
-    public void addGenerationData(Bundle bundle, int key, String indexMapKey) {
+    public void addGenerationData(Bundle bundle, int key) {
         synchronized (mLock) {
-            final MemoryIntArray backingStore = getBackingStoreLocked(key,
-                    /* createIfNotExist= */ true);
-            if (backingStore == null) {
-                // Error accessing existing backing store or no new backing store is available
-                return;
-            }
+            MemoryIntArray backingStore = getBackingStoreLocked();
             try {
-                final int index = getKeyIndexLocked(key, indexMapKey, mKeyToIndexMapMap,
-                        backingStore, /* createIfNotExist= */ true);
-                if (index < 0) {
-                    // Should not happen unless having error accessing the backing store
-                    return;
-                }
-                bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
-                        backingStore);
-                bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
-                bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
-                        backingStore.get(index));
-                if (DEBUG) {
-                    Slog.i(LOG_TAG, "Exported index:" + index
-                            + " for setting:" + indexMapKey
-                            + " key:" + SettingsState.keyToString(key));
+                if (backingStore != null) {
+                    final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore);
+                    if (index >= 0) {
+                        bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
+                                backingStore);
+                        bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
+                        bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
+                                backingStore.get(index));
+                        if (DEBUG) {
+                            Slog.i(LOG_TAG, "Exported index:" + index + " for key:"
+                                    + SettingsProvider.keyToString(key));
+                        }
+                    }
                 }
             } catch (IOException e) {
                 Slog.e(LOG_TAG, "Error adding generation data", e);
-                destroyBackingStoreLocked(key);
+                destroyBackingStore();
             }
         }
     }
 
     public void onUserRemoved(int userId) {
-        final int secureKey = SettingsState.makeKey(
-                SettingsState.SETTINGS_TYPE_SECURE, userId);
-        final int systemKey = SettingsState.makeKey(
-                SettingsState.SETTINGS_TYPE_SYSTEM, userId);
         synchronized (mLock) {
-            if (mKeyToIndexMapMap.containsKey(secureKey)) {
-                destroyBackingStoreLocked(secureKey);
-                mKeyToIndexMapMap.remove(secureKey);
-                mNumBackingStore = mNumBackingStore - 1;
-            }
-            if (mKeyToIndexMapMap.containsKey(systemKey)) {
-                destroyBackingStoreLocked(systemKey);
-                mKeyToIndexMapMap.remove(systemKey);
-                mNumBackingStore = mNumBackingStore - 1;
+            MemoryIntArray backingStore = getBackingStoreLocked();
+            if (backingStore != null && mKeyToIndexMap.size() > 0) {
+                try {
+                    final int secureKey = SettingsProvider.makeKey(
+                            SettingsProvider.SETTINGS_TYPE_SECURE, userId);
+                    resetSlotForKeyLocked(secureKey, mKeyToIndexMap, backingStore);
+
+                    final int systemKey = SettingsProvider.makeKey(
+                            SettingsProvider.SETTINGS_TYPE_SYSTEM, userId);
+                    resetSlotForKeyLocked(systemKey, mKeyToIndexMap, backingStore);
+                } catch (IOException e) {
+                    Slog.e(LOG_TAG, "Error cleaning up for user", e);
+                    destroyBackingStore();
+                }
             }
         }
     }
 
     @GuardedBy("mLock")
-    private MemoryIntArray getBackingStoreLocked(int key, boolean createIfNotExist) {
-        MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key);
-        if (!createIfNotExist) {
-            return backingStore;
-        }
-        if (backingStore == null) {
+    private MemoryIntArray getBackingStoreLocked() {
+        if (mBackingStore == null) {
+            // One for the config table, one for the global table, two for system
+            // and secure tables for a managed profile (managed profile is not
+            // included in the max user count), ten for partially deleted users if
+            // users are quickly removed, and twice max user count for system and
+            // secure.
+            final int size = 1 + 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers();
             try {
-                if (mNumBackingStore >= NUM_MAX_BACKING_STORE) {
-                    Slog.e(LOG_TAG, "Error creating backing store - at capacity");
-                    return null;
-                }
-                backingStore = new MemoryIntArray(MAX_BACKING_STORE_SIZE);
-                mKeyToBackingStoreMap.put(key, backingStore);
-                mNumBackingStore += 1;
+                mBackingStore = new MemoryIntArray(size);
                 if (DEBUG) {
-                    Slog.e(LOG_TAG, "Created backing store for "
-                            + SettingsState.keyToString(key) + " on user: "
-                            + SettingsState.getUserIdFromKey(key));
+                    Slog.e(LOG_TAG, "Created backing store " + mBackingStore);
                 }
             } catch (IOException e) {
                 Slog.e(LOG_TAG, "Error creating generation tracker", e);
             }
         }
-        return backingStore;
+        return mBackingStore;
     }
 
-    @GuardedBy("mLock")
-    private void destroyBackingStoreLocked(int key) {
-        MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key);
-        if (backingStore != null) {
+    private void destroyBackingStore() {
+        if (mBackingStore != null) {
             try {
-                backingStore.close();
+                mBackingStore.close();
                 if (DEBUG) {
-                    Slog.e(LOG_TAG, "Destroyed backing store " + backingStore);
+                    Slog.e(LOG_TAG, "Destroyed backing store " + mBackingStore);
                 }
             } catch (IOException e) {
                 Slog.e(LOG_TAG, "Cannot close generation memory array", e);
             }
-            mKeyToBackingStoreMap.remove(key);
+            mBackingStore = null;
         }
     }
 
-    private static int getKeyIndexLocked(int key, String indexMapKey,
-            ArrayMap<Integer, ArrayMap<String, Integer>> keyToIndexMapMap,
-            MemoryIntArray backingStore, boolean createIfNotExist) throws IOException {
-        ArrayMap<String, Integer> nameToIndexMap = keyToIndexMapMap.get(key);
-        if (nameToIndexMap == null) {
-            if (!createIfNotExist) {
-                return -1;
+    private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap,
+            MemoryIntArray backingStore) throws IOException {
+        final int index = keyToIndexMap.get(key, -1);
+        if (index >= 0) {
+            keyToIndexMap.delete(key);
+            backingStore.set(index, 0);
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Freed index:" + index + " for key:"
+                        + SettingsProvider.keyToString(key));
             }
-            nameToIndexMap = new ArrayMap<>();
-            keyToIndexMapMap.put(key, nameToIndexMap);
         }
-        int index = nameToIndexMap.getOrDefault(indexMapKey, -1);
+    }
+
+    private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap,
+            MemoryIntArray backingStore) throws IOException {
+        int index = keyToIndexMap.get(key, -1);
         if (index < 0) {
-            if (!createIfNotExist) {
-                return -1;
-            }
             index = findNextEmptyIndex(backingStore);
             if (index >= 0) {
                 backingStore.set(index, 1);
-                nameToIndexMap.put(indexMapKey, index);
+                keyToIndexMap.append(key, index);
                 if (DEBUG) {
-                    Slog.i(LOG_TAG, "Allocated index:" + index + " for setting:" + indexMapKey
-                            + " of type:" + SettingsState.keyToString(key)
-                            + " on user:" + SettingsState.getUserIdFromKey(key));
+                    Slog.i(LOG_TAG, "Allocated index:" + index + " for key:"
+                            + SettingsProvider.keyToString(key));
                 }
             } else {
                 Slog.e(LOG_TAG, "Could not allocate generation index");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d7c7130..683c08e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -35,7 +35,6 @@
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM;
 import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
-import static com.android.providers.settings.SettingsState.makeKey;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -376,6 +375,10 @@
     @GuardedBy("mLock")
     private boolean mSyncConfigDisabledUntilReboot;
 
+    public static int makeKey(int type, int userId) {
+        return SettingsState.makeKey(type, userId);
+    }
+
     public static int getTypeFromKey(int key) {
         return SettingsState.getTypeFromKey(key);
     }
@@ -384,6 +387,9 @@
         return SettingsState.getUserIdFromKey(key);
     }
 
+    public static String keyToString(int key) {
+        return SettingsState.keyToString(key);
+    }
     @ChangeId
     @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.S)
     private static final long ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL = 172670679L;
@@ -547,7 +553,7 @@
 
             case Settings.CALL_METHOD_LIST_CONFIG: {
                 String prefix = getSettingPrefix(args);
-                Bundle result = packageValuesForCallResult(prefix, getAllConfigFlags(prefix),
+                Bundle result = packageValuesForCallResult(getAllConfigFlags(prefix),
                         isTrackingGeneration(args));
                 reportDeviceConfigAccess(prefix);
                 return result;
@@ -1313,7 +1319,6 @@
         return false;
     }
 
-    @NonNull
     private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
@@ -2311,8 +2316,7 @@
                 "get/set setting for user", null);
     }
 
-    private Bundle packageValueForCallResult(@Nullable Setting setting,
-            boolean trackingGeneration) {
+    private Bundle packageValueForCallResult(Setting setting, boolean trackingGeneration) {
         if (!trackingGeneration) {
             if (setting == null || setting.isNull()) {
                 return NULL_SETTING_BUNDLE;
@@ -2323,26 +2327,19 @@
         result.putString(Settings.NameValueTable.VALUE,
                 !setting.isNull() ? setting.getValue() : null);
 
-        if (setting != null && !setting.isNull()) {
-            // No need to track generation if the setting doesn't exist
-            synchronized (mLock) {
-                mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey(),
-                        setting.getName());
-            }
-        }
+        mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey());
         return result;
     }
 
-    private Bundle packageValuesForCallResult(String prefix,
-            @NonNull HashMap<String, String> keyValues, boolean trackingGeneration) {
+    private Bundle packageValuesForCallResult(HashMap<String, String> keyValues,
+            boolean trackingGeneration) {
         Bundle result = new Bundle();
         result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
-        if (trackingGeneration && !keyValues.isEmpty()) {
-            // No need to track generation if the namespace is empty
+        if (trackingGeneration) {
             synchronized (mLock) {
                 mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
-                        mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG,
-                                UserHandle.USER_SYSTEM).mKey, prefix);
+                        mSettingsRegistry.getSettingsLocked(
+                                SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM).mKey);
             }
         }
 
@@ -3452,7 +3449,7 @@
 
         private void notifyForSettingsChange(int key, String name) {
             // Increment the generation first, so observers always see the new value
-            mGenerationRegistry.incrementGeneration(key, name);
+            mGenerationRegistry.incrementGeneration(key);
 
             if (isGlobalSettingsKey(key) || isConfigSettingsKey(key)) {
                 final long token = Binder.clearCallingIdentity();
@@ -3490,7 +3487,7 @@
                 List<String> changedSettings) {
 
             // Increment the generation first, so observers always see the new value
-            mGenerationRegistry.incrementGeneration(key, prefix);
+            mGenerationRegistry.incrementGeneration(key);
 
             StringBuilder stringBuilder = new StringBuilder(prefix);
             for (int i = 0; i < changedSettings.size(); ++i) {
@@ -3516,7 +3513,7 @@
                     if (profileId != userId) {
                         final int key = makeKey(type, profileId);
                         // Increment the generation first, so observers always see the new value
-                        mGenerationRegistry.incrementGeneration(key, name);
+                        mGenerationRegistry.incrementGeneration(key);
                         mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
                                 profileId, 0, uri).sendToTarget();
                     }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
deleted file mode 100644
index d34fe694..0000000
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.settings;
-
-import static android.provider.Settings.CALL_METHOD_GENERATION_INDEX_KEY;
-import static android.provider.Settings.CALL_METHOD_GENERATION_KEY;
-import static android.provider.Settings.CALL_METHOD_TRACK_GENERATION_KEY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.util.MemoryIntArray;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-public class GenerationRegistryTest {
-    @Test
-    public void testGenerationsWithRegularSetting() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
-        final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
-        final String testSecureSetting = "test_secure_setting";
-        Bundle b = new Bundle();
-        // IncrementGeneration should have no effect on a non-cached setting.
-        generationRegistry.incrementGeneration(secureKey, testSecureSetting);
-        generationRegistry.incrementGeneration(secureKey, testSecureSetting);
-        generationRegistry.incrementGeneration(secureKey, testSecureSetting);
-        generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
-        // Default index is 0 and generation is only 1 despite early calls of incrementGeneration
-        checkBundle(b, 0, 1, false);
-
-        generationRegistry.incrementGeneration(secureKey, testSecureSetting);
-        generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
-        // Index is still 0 and generation is now 2; also check direct array access
-        assertThat(getArray(b).get(0)).isEqualTo(2);
-
-        final int systemKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SYSTEM, 0);
-        final String testSystemSetting = "test_system_setting";
-        generationRegistry.addGenerationData(b, systemKey, testSystemSetting);
-        // Default index is 0 and generation is 1 for another backingStore (system)
-        checkBundle(b, 0, 1, false);
-
-        final String testSystemSetting2 = "test_system_setting2";
-        generationRegistry.addGenerationData(b, systemKey, testSystemSetting2);
-        // Second system setting index is 1 and default generation is 1
-        checkBundle(b, 1, 1, false);
-
-        generationRegistry.incrementGeneration(systemKey, testSystemSetting);
-        generationRegistry.incrementGeneration(systemKey, testSystemSetting);
-        generationRegistry.addGenerationData(b, systemKey, testSystemSetting);
-        // First system setting generation now incremented to 3
-        checkBundle(b, 0, 3, false);
-
-        final int systemKey2 = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SYSTEM, 10);
-        generationRegistry.addGenerationData(b, systemKey2, testSystemSetting);
-        // User 10 has a new set of backingStores
-        checkBundle(b, 0, 1, false);
-
-        // Check user removal
-        generationRegistry.onUserRemoved(10);
-        generationRegistry.incrementGeneration(systemKey2, testSystemSetting);
-
-        // Removed user should not affect existing caches
-        generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
-        assertThat(getArray(b).get(0)).isEqualTo(2);
-
-        // IncrementGeneration should have no effect for a non-cached user
-        b.clear();
-        checkBundle(b, -1, -1, true);
-        // AddGeneration should create new backing store for the non-cached user
-        generationRegistry.addGenerationData(b, systemKey2, testSystemSetting);
-        checkBundle(b, 0, 1, false);
-    }
-
-    @Test
-    public void testGenerationsWithConfigSetting() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
-        final String prefix = "test_namespace/";
-        final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
-
-        Bundle b = new Bundle();
-        generationRegistry.addGenerationData(b, configKey, prefix);
-        checkBundle(b, 0, 1, false);
-
-        final String setting = "test_namespace/test_setting";
-        // Check that the generation of the prefix is incremented correctly
-        generationRegistry.incrementGeneration(configKey, setting);
-        generationRegistry.addGenerationData(b, configKey, prefix);
-        checkBundle(b, 0, 2, false);
-    }
-
-    @Test
-    public void testMaxNumBackingStores() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
-        final String testSecureSetting = "test_secure_setting";
-        Bundle b = new Bundle();
-        for (int i = 0; i < GenerationRegistry.NUM_MAX_BACKING_STORE; i++) {
-            b.clear();
-            final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, i);
-            generationRegistry.addGenerationData(b, key, testSecureSetting);
-            checkBundle(b, 0, 1, false);
-        }
-        b.clear();
-        final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE,
-                GenerationRegistry.NUM_MAX_BACKING_STORE + 1);
-        generationRegistry.addGenerationData(b, key, testSecureSetting);
-        // Should fail to add generation because the number of backing stores has reached limit
-        checkBundle(b, -1, -1, true);
-        // Remove one user should free up a backing store
-        generationRegistry.onUserRemoved(0);
-        generationRegistry.addGenerationData(b, key, testSecureSetting);
-        checkBundle(b, 0, 1, false);
-    }
-
-    @Test
-    public void testMaxSizeBackingStore() throws IOException {
-        final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
-        final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
-        final String testSecureSetting = "test_secure_setting";
-        Bundle b = new Bundle();
-        for (int i = 0; i < GenerationRegistry.MAX_BACKING_STORE_SIZE; i++) {
-            generationRegistry.addGenerationData(b, secureKey, testSecureSetting + i);
-            checkBundle(b, i, 1, false);
-        }
-        b.clear();
-        generationRegistry.addGenerationData(b, secureKey, testSecureSetting);
-        // Should fail to increase index because the number of entries in the backing store has
-        // reached the limit
-        checkBundle(b, -1, -1, true);
-        // Shouldn't affect other cached entries
-        generationRegistry.addGenerationData(b, secureKey, testSecureSetting + "0");
-        checkBundle(b, 0, 1, false);
-    }
-
-    private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
-            throws IOException {
-        final MemoryIntArray array = getArray(b);
-        if (isNull) {
-            assertThat(array).isNull();
-        } else {
-            assertThat(array).isNotNull();
-        }
-        final int index = b.getInt(
-                CALL_METHOD_GENERATION_INDEX_KEY, -1);
-        assertThat(index).isEqualTo(expectedIndex);
-        final int generation = b.getInt(CALL_METHOD_GENERATION_KEY, -1);
-        assertThat(generation).isEqualTo(expectedGeneration);
-        if (!isNull) {
-            // Read into the result array with the result index should match the result generation
-            assertThat(array.get(index)).isEqualTo(generation);
-        }
-    }
-
-    private MemoryIntArray getArray(Bundle b) {
-        return b.getParcelable(
-                CALL_METHOD_TRACK_GENERATION_KEY, android.util.MemoryIntArray.class);
-    }
-}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index dd0a4f0..a25790a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.accessibility.accessibilitymenu.view;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static java.lang.Math.max;
 
 import android.animation.Animator;
@@ -133,7 +135,12 @@
             initLayoutParams();
         }
 
-        mLayout = new FrameLayout(mService);
+        final Display display = mService.getSystemService(
+                DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+
+        mLayout = new FrameLayout(
+                mService.createDisplayContext(display).createWindowContext(
+                        WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, null));
         updateLayoutPosition();
         inflateLayoutAndSetOnTouchListener(mLayout);
         mA11yMenuViewPager = new A11yMenuViewPager(mService);
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 8ba1ff3..37b8ae0 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -76,7 +76,10 @@
                     android:layout_height="48dp"
                     android:layout_width="wrap_content"
                     android:layout_gravity="center_vertical"
-                    android:padding="8dp" />
+                    android:padding="8dp"
+                    android:track="@drawable/settingslib_track_selector"
+                    android:thumb="@drawable/settingslib_thumb_selector"
+                    android:theme="@style/MainSwitch.Settingslib"/>
             </com.android.systemui.statusbar.notification.row.AppControlView>
 
             <!-- ChannelRows get added dynamically -->
@@ -101,7 +104,7 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
-                style="@style/TextAppearance.NotificationInfo.Button"/>
+                style="@style/Widget.Dialog.Button"/>
             <TextView
                 android:id="@+id/done_button"
                 android:text="@string/inline_ok_button"
@@ -113,7 +116,7 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:layout_alignParentEnd="true"
-                style="@style/TextAppearance.NotificationInfo.Button"/>
+                style="@style/Widget.Dialog.Button"/>
         </RelativeLayout>
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index d03cd7e..190f999 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -85,6 +85,9 @@
             android:layout_width="wrap_content"
             android:layout_gravity="center_vertical"
             android:padding="8dp"
+            android:track="@drawable/settingslib_track_selector"
+            android:thumb="@drawable/settingslib_thumb_selector"
+            android:theme="@style/MainSwitch.Settingslib"
         />
     </LinearLayout>
 </com.android.systemui.statusbar.notification.row.ChannelRow>
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 8690b36..b927155 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
@@ -62,7 +62,7 @@
      */
     public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
             int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
-            int taskbarSize, boolean isLargeScreen,
+            int taskbarSize, boolean isTablet,
             int currentRotation, boolean isRtl) {
         boolean isRotated = false;
         boolean isOrientationDifferent;
@@ -95,7 +95,7 @@
             canvasScreenRatio = (float) canvasWidth / screenWidthPx;
         }
         scaledTaskbarSize = taskbarSize * canvasScreenRatio;
-        thumbnailClipHint.bottom = isLargeScreen ? scaledTaskbarSize : 0;
+        thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
 
         float scale = thumbnailData.scale;
         final float thumbnailScale;
@@ -103,7 +103,7 @@
         // Landscape vs portrait change.
         // Note: Disable rotation in grid layout.
         boolean windowingModeSupportsRotation =
-                thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isLargeScreen;
+                thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isTablet;
         isOrientationDifferent = isOrientationChange(deltaRotate)
                 && windowingModeSupportsRotation;
         if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 751a3f8..77a13bd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -137,7 +137,7 @@
 
     /** @return whether or not {@param context} represents that of a large screen device or not */
     @TargetApi(Build.VERSION_CODES.R)
-    public static boolean isLargeScreen(Context context) {
+    public static boolean isTablet(Context context) {
         final WindowManager windowManager = context.getSystemService(WindowManager.class);
         final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 2a37bd3..80b9758 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -36,7 +36,6 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.IRecentsAnimationController;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -106,12 +105,24 @@
         private RecentsAnimationListener mListener = null;
         private RecentsAnimationControllerCompat mWrapped = null;
         private IRemoteTransitionFinishedCallback mFinishCB = null;
+
+        /**
+         * List of tasks that we are switching away from via this transition. Upon finish, these
+         * pausing tasks will become invisible.
+         * These need to be ordered since the order must be restored if there is no task-switch.
+         */
         private ArrayList<TaskState> mPausingTasks = null;
+
+        /**
+         * List of tasks that we are switching to. Upon finish, these will remain visible and
+         * on top.
+         */
+        private ArrayList<TaskState> mOpeningTasks = null;
+
         private WindowContainerToken mPipTask = null;
         private WindowContainerToken mRecentsTask = null;
         private int mRecentsTaskId = 0;
         private TransitionInfo mInfo = null;
-        private ArrayList<SurfaceControl> mOpeningLeashes = null;
         private boolean mOpeningSeparateHome = false;
         private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
         private PictureInPictureSurfaceTransaction mPipTransaction = null;
@@ -120,6 +131,15 @@
         private RemoteAnimationTarget[] mAppearedTargets;
         private boolean mWillFinishToHome = false;
 
+        /** The animation is idle, waiting for the user to choose a task to switch to. */
+        private static final int STATE_NORMAL = 0;
+
+        /** The user chose a new task to switch to and the animation is animating to it. */
+        private static final int STATE_NEW_TASK = 1;
+
+        /** The latest state that the recents animation is operating in. */
+        private int mState = STATE_NORMAL;
+
         void start(RecentsAnimationControllerCompat wrapped, RecentsAnimationListener listener,
                 IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
                 IRemoteTransitionFinishedCallback finishedCallback) {
@@ -132,12 +152,14 @@
             mInfo = info;
             mFinishCB = finishedCallback;
             mPausingTasks = new ArrayList<>();
+            mOpeningTasks = new ArrayList<>();
             mPipTask = null;
             mRecentsTask = null;
             mRecentsTaskId = -1;
             mLeashMap = new ArrayMap<>();
             mTransition = transition;
             mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
+            mState = STATE_NORMAL;
 
             final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
             final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
@@ -178,6 +200,9 @@
                     } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
                         mRecentsTask = taskInfo.token;
                         mRecentsTaskId = taskInfo.taskId;
+                    } else if (change.getMode() == TRANSIT_OPEN
+                            || change.getMode() == TRANSIT_TO_FRONT) {
+                        mOpeningTasks.add(new TaskState(change, target.leash));
                     }
                 }
             }
@@ -189,34 +214,41 @@
 
         @SuppressLint("NewApi")
         boolean merge(TransitionInfo info, SurfaceControl.Transaction t) {
-            SparseArray<TransitionInfo.Change> openingTasks = null;
+            ArrayList<TransitionInfo.Change> openingTasks = null;
+            ArrayList<TransitionInfo.Change> closingTasks = null;
             mAppearedTargets = null;
-            boolean foundHomeOpening = false;
+            mOpeningSeparateHome = false;
+            TransitionInfo.Change recentsOpening = null;
             boolean foundRecentsClosing = false;
             boolean hasChangingApp = false;
-            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final RemoteAnimationTargetCompat.LeafTaskFilter leafTaskFilter =
+                    new RemoteAnimationTargetCompat.LeafTaskFilter();
+            for (int i = 0; i < info.getChanges().size(); ++i) {
                 final TransitionInfo.Change change = info.getChanges().get(i);
+                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+                final boolean isLeafTask = leafTaskFilter.test(change);
                 if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
-                    final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-                    if (taskInfo != null) {
+                    if (mRecentsTask.equals(change.getContainer())) {
+                        recentsOpening = change;
+                    } else if (isLeafTask) {
                         if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
                             // This is usually a 3p launcher
-                            foundHomeOpening = true;
+                            mOpeningSeparateHome = true;
                         }
                         if (openingTasks == null) {
-                            openingTasks = new SparseArray<>();
+                            openingTasks = new ArrayList<>();
                         }
-                        if (taskInfo.hasParentTask()) {
-                            // Collects opening leaf tasks only since Launcher monitors leaf task
-                            // ids to perform recents animation.
-                            openingTasks.remove(taskInfo.parentTaskId);
-                        }
-                        openingTasks.put(taskInfo.taskId, change);
+                        openingTasks.add(change);
                     }
                 } else if (change.getMode() == TRANSIT_CLOSE
                         || change.getMode() == TRANSIT_TO_BACK) {
                     if (mRecentsTask.equals(change.getContainer())) {
                         foundRecentsClosing = true;
+                    } else if (isLeafTask) {
+                        if (closingTasks == null) {
+                            closingTasks = new ArrayList<>();
+                        }
+                        closingTasks.add(change);
                     }
                 } else if (change.getMode() == TRANSIT_CHANGE) {
                     hasChangingApp = true;
@@ -234,45 +266,72 @@
                 }
                 return false;
             }
-            if (openingTasks == null) return false;
-            int pauseMatches = 0;
-            if (!foundHomeOpening) {
+            if (recentsOpening != null) {
+                // the recents task re-appeared. This happens if the user gestures before the
+                // task-switch (NEW_TASK) animation finishes.
+                if (mState == STATE_NORMAL) {
+                    Log.e(TAG, "Returning to recents while recents is already idle.");
+                }
+                if (closingTasks == null || closingTasks.size() == 0) {
+                    Log.e(TAG, "Returning to recents without closing any opening tasks.");
+                }
+                // Setup may hide it initially since it doesn't know that overview was still active.
+                t.show(recentsOpening.getLeash());
+                t.setAlpha(recentsOpening.getLeash(), 1.f);
+                mState = STATE_NORMAL;
+            }
+            boolean didMergeThings = false;
+            if (closingTasks != null) {
+                // Cancelling a task-switch. Move the tasks back to mPausing from mOpening
+                for (int i = 0; i < closingTasks.size(); ++i) {
+                    final TransitionInfo.Change change = closingTasks.get(i);
+                    int openingIdx = TaskState.indexOf(mOpeningTasks, change);
+                    if (openingIdx < 0) {
+                        Log.e(TAG, "Back to existing recents animation from an unrecognized "
+                                + "task: " + change.getTaskInfo().taskId);
+                        continue;
+                    }
+                    mPausingTasks.add(mOpeningTasks.remove(openingIdx));
+                    didMergeThings = true;
+                }
+            }
+            if (openingTasks != null && openingTasks.size() > 0) {
+                // Switching to some new tasks, add to mOpening and remove from mPausing. Also,
+                // enter NEW_TASK state since this will start the switch-to animation.
+                final int layer = mInfo.getChanges().size() * 3;
+                final RemoteAnimationTarget[] targets =
+                        new RemoteAnimationTarget[openingTasks.size()];
                 for (int i = 0; i < openingTasks.size(); ++i) {
-                    if (TaskState.indexOf(mPausingTasks, openingTasks.valueAt(i)) >= 0) {
-                        ++pauseMatches;
+                    final TransitionInfo.Change change = openingTasks.get(i);
+                    int pausingIdx = TaskState.indexOf(mPausingTasks, change);
+                    if (pausingIdx >= 0) {
+                        // Something is showing/opening a previously-pausing app.
+                        targets[i] = newTarget(change, layer, mPausingTasks.get(pausingIdx).mLeash);
+                        mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
+                        // Setup hides opening tasks initially, so make it visible again (since we
+                        // are already showing it).
+                        t.show(change.getLeash());
+                        t.setAlpha(change.getLeash(), 1.f);
+                    } else {
+                        // We are receiving new opening tasks, so convert to onTasksAppeared.
+                        targets[i] = newTarget(change, layer, info, t, mLeashMap);
+                        t.reparent(targets[i].leash, mInfo.getRootLeash());
+                        t.setLayer(targets[i].leash, layer);
+                        mOpeningTasks.add(new TaskState(change, targets[i].leash));
                     }
                 }
+                didMergeThings = true;
+                mState = STATE_NEW_TASK;
+                mAppearedTargets = targets;
             }
-            if (pauseMatches > 0) {
-                if (pauseMatches != mPausingTasks.size()) {
-                    // We are not really "returning" properly... something went wrong.
-                    throw new IllegalStateException("\"Concelling\" a recents transitions by "
-                            + "unpausing " + pauseMatches + " apps after pausing "
-                            + mPausingTasks.size() + " apps.");
-                }
-                // In this case, we are "returning" to an already running app, so just consume
-                // the merge and do nothing.
-                info.releaseAllSurfaces();
-                t.close();
-                return true;
-            }
-            final int layer = mInfo.getChanges().size() * 3;
-            mOpeningLeashes = new ArrayList<>();
-            mOpeningSeparateHome = foundHomeOpening;
-            final RemoteAnimationTarget[] targets =
-                    new RemoteAnimationTarget[openingTasks.size()];
-            for (int i = 0; i < openingTasks.size(); ++i) {
-                final TransitionInfo.Change change = openingTasks.valueAt(i);
-                mOpeningLeashes.add(change.getLeash());
-                // We are receiving new opening tasks, so convert to onTasksAppeared.
-                targets[i] = newTarget(change, layer, info, t, mLeashMap);
-                t.reparent(targets[i].leash, mInfo.getRootLeash());
-                t.setLayer(targets[i].leash, layer);
+            if (!didMergeThings) {
+                // Didn't recognize anything in incoming transition so don't merge it.
+                Log.w(TAG, "Don't know how to merge this transition.");
+                return false;
             }
             t.apply();
             // not using the incoming anim-only surfaces
             info.releaseAnimSurfaces();
-            mAppearedTargets = targets;
             return true;
         }
 
@@ -300,6 +359,8 @@
             if (enabled) {
                 // transient launches don't receive focus automatically. Since we are taking over
                 // the gesture now, take focus explicitly.
+                // This also moves recents back to top if the user gestured before a switch
+                // animation finished.
                 try {
                     ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
                 } catch (RemoteException e) {
@@ -336,8 +397,8 @@
                 if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
                 else wct.restoreTransientOrder(mRecentsTask);
             }
-            if (!toHome && !mWillFinishToHome && mPausingTasks != null && mOpeningLeashes == null) {
-                // The gesture went back to opening the app rather than continuing with
+            if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+                // The gesture is returning to the pausing-task(s) rather than continuing with
                 // recents, so end the transition by moving the app back to the top (and also
                 // re-showing it's task).
                 for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
@@ -349,25 +410,28 @@
                     wct.restoreTransientOrder(mRecentsTask);
                 }
             } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
-                // Special situaition where 3p launcher was changed during recents (this happens
+                // Special situation where 3p launcher was changed during recents (this happens
                 // during tapltests...). Here we get both "return to home" AND "home opening".
-                // This is basically going home, but we have to restore recents order and also
-                // treat the home "pausing" task properly.
-                for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
-                    final TaskState state = mPausingTasks.get(i);
+                // This is basically going home, but we have to restore the recents and home order.
+                for (int i = 0; i < mOpeningTasks.size(); ++i) {
+                    final TaskState state = mOpeningTasks.get(i);
                     if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
-                        // Treat as opening (see above)
+                        // Make sure it is on top.
                         wct.reorder(state.mToken, true /* onTop */);
-                        t.show(state.mTaskSurface);
-                    } else {
-                        // Treat as hiding (see below)
-                        t.hide(state.mTaskSurface);
                     }
+                    t.show(state.mTaskSurface);
+                }
+                for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+                    t.hide(mPausingTasks.get(i).mTaskSurface);
                 }
                 if (!mKeyguardLocked && mRecentsTask != null) {
                     wct.restoreTransientOrder(mRecentsTask);
                 }
             } else {
+                // The general case: committing to recents, going home, or switching tasks.
+                for (int i = 0; i < mOpeningTasks.size(); ++i) {
+                    t.show(mOpeningTasks.get(i).mTaskSurface);
+                }
                 for (int i = 0; i < mPausingTasks.size(); ++i) {
                     if (!sendUserLeaveHint) {
                         // This means recents is not *actually* finishing, so of course we gotta
@@ -391,7 +455,7 @@
             try {
                 mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t);
             } catch (RemoteException e) {
-                Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
+                Log.e(TAG, "Failed to call animation finish callback", e);
                 t.apply();
             }
             // Only release the non-local created surface references. The animator is responsible
@@ -402,12 +466,13 @@
             mListener = null;
             mFinishCB = null;
             mPausingTasks = null;
+            mOpeningTasks = null;
             mAppearedTargets = null;
             mInfo = null;
-            mOpeningLeashes = null;
             mOpeningSeparateHome = false;
             mLeashMap = null;
             mTransition = null;
+            mState = STATE_NORMAL;
         }
 
         @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index a25b281..c5a06b4 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -24,7 +24,6 @@
 import android.widget.Button;
 
 import com.android.internal.util.EmergencyAffordanceManager;
-import com.android.internal.widget.LockPatternUtils;
 
 /**
  * This class implements a smart emergency button that updates itself based
@@ -40,8 +39,6 @@
     private int mDownY;
     private boolean mLongPressWasDragged;
 
-    private LockPatternUtils mLockPatternUtils;
-
     private final boolean mEnableEmergencyCallWhileSimLocked;
 
     public EmergencyButton(Context context) {
@@ -58,7 +55,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mLockPatternUtils = new LockPatternUtils(mContext);
         if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
             setOnLongClickListener(v -> {
                 if (!mLongPressWasDragged
@@ -95,7 +91,8 @@
         return super.performLongClick();
     }
 
-    void updateEmergencyCallButton(boolean isInCall, boolean hasTelephonyRadio, boolean simLocked) {
+    void updateEmergencyCallButton(boolean isInCall, boolean hasTelephonyRadio, boolean simLocked,
+            boolean isSecure) {
         boolean visible = false;
         if (hasTelephonyRadio) {
             // Emergency calling requires a telephony radio.
@@ -107,7 +104,7 @@
                     visible = mEnableEmergencyCallWhileSimLocked;
                 } else {
                     // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk);
-                    visible = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser());
+                    visible = isSecure;
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index ea808eb..f7e8eb4 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.annotation.SuppressLint;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.content.Intent;
@@ -31,16 +32,22 @@
 import android.util.Log;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.util.EmergencyDialerConstants;
 import com.android.systemui.util.ViewController;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 /** View Controller for {@link com.android.keyguard.EmergencyButton}. */
@@ -57,6 +64,9 @@
     private final MetricsLogger mMetricsLogger;
 
     private EmergencyButtonCallback mEmergencyButtonCallback;
+    private LockPatternUtils mLockPatternUtils;
+    private Executor mMainExecutor;
+    private Executor mBackgroundExecutor;
 
     private final KeyguardUpdateMonitorCallback mInfoCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -78,12 +88,15 @@
         }
     };
 
-    private EmergencyButtonController(@Nullable EmergencyButton view,
+    @VisibleForTesting
+    public EmergencyButtonController(@Nullable EmergencyButton view,
             ConfigurationController configurationController,
             KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager,
             PowerManager powerManager, ActivityTaskManager activityTaskManager,
             ShadeController shadeController,
-            @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger) {
+            @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
+            LockPatternUtils lockPatternUtils,
+            Executor mainExecutor, Executor backgroundExecutor) {
         super(view);
         mConfigurationController = configurationController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -93,6 +106,9 @@
         mShadeController = shadeController;
         mTelecomManager = telecomManager;
         mMetricsLogger = metricsLogger;
+        mLockPatternUtils = lockPatternUtils;
+        mMainExecutor = mainExecutor;
+        mBackgroundExecutor = backgroundExecutor;
     }
 
     @Override
@@ -113,13 +129,27 @@
         mConfigurationController.removeCallback(mConfigurationListener);
     }
 
-    private void updateEmergencyCallButton() {
+    /**
+     * Updates the visibility of the emergency button.
+     *
+     * This method runs binder calls in a background thread.
+     */
+    @VisibleForTesting
+    @SuppressLint("MissingPermission")
+    public void updateEmergencyCallButton() {
         if (mView != null) {
-            mView.updateEmergencyCallButton(
-                    mTelecomManager != null && mTelecomManager.isInCall(),
-                    getContext().getPackageManager().hasSystemFeature(
-                            PackageManager.FEATURE_TELEPHONY),
-                    mKeyguardUpdateMonitor.isSimPinVoiceSecure());
+            // Run in bg thread to avoid throttling the main thread with binder call.
+            mBackgroundExecutor.execute(() -> {
+                boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall();
+                boolean isSecure = mLockPatternUtils
+                        .isSecure(KeyguardUpdateMonitor.getCurrentUser());
+                mMainExecutor.execute(() -> mView.updateEmergencyCallButton(
+                        /* isInCall= */ isInCall,
+                        /* hasTelephonyRadio= */ getContext().getPackageManager()
+                                .hasSystemFeature(PackageManager.FEATURE_TELEPHONY),
+                        /* simLocked= */ mKeyguardUpdateMonitor.isSimPinVoiceSecure(),
+                        /* isSecure= */ isSecure));
+            });
         }
     }
 
@@ -129,6 +159,7 @@
     /**
      * Shows the emergency dialer or returns the user to the existing call.
      */
+    @SuppressLint("MissingPermission")
     public void takeEmergencyCallAction() {
         mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL);
         if (mPowerManager != null) {
@@ -136,29 +167,35 @@
         }
         mActivityTaskManager.stopSystemLockTaskMode();
         mShadeController.collapseShade(false);
-        if (mTelecomManager != null && mTelecomManager.isInCall()) {
-            mTelecomManager.showInCallScreen(false);
-            if (mEmergencyButtonCallback != null) {
-                mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
-            }
-        } else {
-            mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */);
-            if (mTelecomManager == null) {
-                Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer");
-                return;
-            }
-            Intent emergencyDialIntent =
-                    mTelecomManager.createLaunchEmergencyDialerIntent(null /* number*/)
-                            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                                    | Intent.FLAG_ACTIVITY_CLEAR_TOP)
-                            .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
-                                    EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
+        // Run in bg thread to avoid throttling the main thread with binder call.
+        mBackgroundExecutor.execute(() -> {
+            boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall();
+            mMainExecutor.execute(() -> {
+                if (isInCall) {
+                    mTelecomManager.showInCallScreen(false);
+                    if (mEmergencyButtonCallback != null) {
+                        mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
+                    }
+                } else {
+                    mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */);
+                    if (mTelecomManager == null) {
+                        Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer");
+                        return;
+                    }
+                    Intent emergencyDialIntent =
+                            mTelecomManager.createLaunchEmergencyDialerIntent(null /* number*/)
+                                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                                            | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                                    .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+                                            EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
 
-            getContext().startActivityAsUser(emergencyDialIntent,
-                    ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(),
-                    new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
-        }
+                    getContext().startActivityAsUser(emergencyDialIntent,
+                            ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(),
+                            new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
+                }
+            });
+        });
     }
 
     /** */
@@ -178,13 +215,19 @@
         @Nullable
         private final TelecomManager mTelecomManager;
         private final MetricsLogger mMetricsLogger;
+        private final LockPatternUtils mLockPatternUtils;
+        private final Executor mMainExecutor;
+        private final Executor mBackgroundExecutor;
 
         @Inject
         public Factory(ConfigurationController configurationController,
                 KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager,
                 PowerManager powerManager, ActivityTaskManager activityTaskManager,
                 ShadeController shadeController,
-                @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger) {
+                @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
+                LockPatternUtils lockPatternUtils,
+                @Main Executor mainExecutor,
+                @Background Executor backgroundExecutor) {
 
             mConfigurationController = configurationController;
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -194,14 +237,17 @@
             mShadeController = shadeController;
             mTelecomManager = telecomManager;
             mMetricsLogger = metricsLogger;
+            mLockPatternUtils = lockPatternUtils;
+            mMainExecutor = mainExecutor;
+            mBackgroundExecutor = backgroundExecutor;
         }
 
         /** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
         public EmergencyButtonController create(EmergencyButton view) {
             return new EmergencyButtonController(view, mConfigurationController,
                     mKeyguardUpdateMonitor, mTelephonyManager, mPowerManager, mActivityTaskManager,
-                    mShadeController,
-                    mTelecomManager, mMetricsLogger);
+                    mShadeController, mTelecomManager, mMetricsLogger, mLockPatternUtils,
+                    mMainExecutor, mBackgroundExecutor);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 0a4880e..3b0644e 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -33,7 +33,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
@@ -46,7 +45,6 @@
 
     private final TextView mDigitText;
     private final TextView mKlondikeText;
-    private final LockPatternUtils mLockPatternUtils;
     private final PowerManager mPM;
 
     private int mDigit = -1;
@@ -107,7 +105,6 @@
         setOnHoverListener(new LiftToActivateListener(
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
 
-        mLockPatternUtils = new LockPatternUtils(context);
         mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 98a3e4b..cef415c 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -130,8 +130,7 @@
         animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
 
         // For tablet docking animation, we don't play the background scrim.
-        // TODO(b/270524780): use utility to check for tablet instead. 
-        if (!Utilities.isLargeScreen(context)) {
+        if (!Utilities.isTablet(context)) {
             ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
                     "backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
             scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
index f5bbba7..776b7bd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
@@ -34,7 +34,7 @@
 
     @Override
     public void show() {
-        mStatusBarKeyguardViewManager.showBouncer(false);
+        mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a9f8c9c..ab78b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -246,6 +246,9 @@
             "qs_user_detail_shortcut"
         )
 
+    @JvmField
+    val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = false)
+
     // TODO(b/254512383): Tracking Bug
     @JvmField
     val FULL_SCREEN_USER_SWITCHER =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 57c4b36..0ca9115 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -945,9 +945,9 @@
             return false
         }
 
-        // We don't do the shared element on large screens because the smartspace has to fly across
-        // large distances, which is distracting.
-        if (Utilities.isLargeScreen(context)) {
+        // We don't do the shared element on tablets because they're large and the smartspace has to
+        // fly across large distances, which is distracting.
+        if (Utilities.isTablet(context)) {
             return false
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index dfe1038..0140529 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -33,6 +34,7 @@
 class AlternateBouncerInteractor
 @Inject
 constructor(
+    private val keyguardStateController: KeyguardStateController,
     private val bouncerRepository: KeyguardBouncerRepository,
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
@@ -102,7 +104,8 @@
                 biometricSettingsRepository.isFingerprintEnrolled.value &&
                 biometricSettingsRepository.isStrongBiometricAllowed.value &&
                 biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
-                !deviceEntryFingerprintAuthRepository.isLockedOut.value
+                !deviceEntryFingerprintAuthRepository.isLockedOut.value &&
+                !keyguardStateController.isUnlocked
         } else {
             legacyAlternateBouncer != null &&
                 keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 9b9d561..d4991f9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.shared.recents.model.ThumbnailData
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
-import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
+import com.android.systemui.shared.recents.utilities.Utilities.isTablet
 
 /**
  * Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData].
@@ -150,9 +150,9 @@
         val displayWidthPx = windowMetrics.bounds.width()
         val displayHeightPx = windowMetrics.bounds.height()
         val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
-        val isLargeScreen = isLargeScreen(context)
+        val isTablet = isTablet(context)
         val taskbarSize =
-            if (isLargeScreen) {
+            if (isTablet) {
                 resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
             } else {
                 0
@@ -166,7 +166,7 @@
             displayWidthPx,
             displayHeightPx,
             taskbarSize,
-            isLargeScreen,
+            isTablet,
             currentRotation,
             isRtl
         )
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 1c90154..88d5eaa 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -23,7 +23,7 @@
 import com.android.internal.R as AndroidR
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
-import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
+import com.android.systemui.shared.recents.utilities.Utilities.isTablet
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -61,8 +61,8 @@
         val width = windowMetrics.bounds.width()
         var height = maximumWindowHeight
 
-        val isLargeScreen = isLargeScreen(context)
-        if (isLargeScreen) {
+        val isTablet = isTablet(context)
+        if (isTablet) {
             val taskbarSize =
                 context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
             height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 4db1da3..d5d7325 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -41,7 +41,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -1792,7 +1792,7 @@
 
     private void setNavigationIconHints(int hints) {
         if (hints == mNavigationIconHints) return;
-        if (!isLargeScreen(mContext)) {
+        if (!isTablet(mContext)) {
             // All IME functions handled by launcher via Sysui flags for large screen
             final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
             final boolean oldBackAlt =
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 63d977e..3c17465 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -21,7 +21,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -91,7 +91,7 @@
     private final DisplayManager mDisplayManager;
     private final TaskbarDelegate mTaskbarDelegate;
     private int mNavMode;
-    @VisibleForTesting boolean mIsLargeScreen;
+    @VisibleForTesting boolean mIsTablet;
 
     /** A displayId - nav bar maps. */
     @VisibleForTesting
@@ -138,16 +138,16 @@
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
                 dumpManager, autoHideController, lightBarController, pipOptional,
                 backAnimation.orElse(null), taskStackChangeListeners);
-        mIsLargeScreen = isLargeScreen(mContext);
+        mIsTablet = isTablet(mContext);
         dumpManager.registerDumpable(this);
     }
 
     @Override
     public void onConfigChanged(Configuration newConfig) {
-        boolean isOldConfigLargeScreen = mIsLargeScreen;
-        mIsLargeScreen = isLargeScreen(mContext);
+        boolean isOldConfigTablet = mIsTablet;
+        mIsTablet = isTablet(mContext);
         boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
-        boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
+        boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
         // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
         Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
                 + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
@@ -235,9 +235,8 @@
 
     /** @return {@code true} if taskbar is enabled, false otherwise */
     private boolean initializeTaskbarIfNecessary() {
-        // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
-        boolean taskbarEnabled = mIsLargeScreen || mFeatureFlags.isEnabled(
-                Flags.HIDE_NAVBAR_WINDOW);
+        // Enable for tablet or (phone AND flag is set); assuming phone = !mIsTablet
+        boolean taskbarEnabled = mIsTablet || mFeatureFlags.isEnabled(Flags.HIDE_NAVBAR_WINDOW);
 
         if (taskbarEnabled) {
             Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
@@ -259,7 +258,7 @@
     @Override
     public void onDisplayReady(int displayId) {
         Display display = mDisplayManager.getDisplay(displayId);
-        mIsLargeScreen = isLargeScreen(mContext);
+        mIsTablet = isTablet(mContext);
         createNavigationBar(display, null /* savedState */, null /* result */);
     }
 
@@ -471,7 +470,7 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("mIsLargeScreen=" + mIsLargeScreen);
+        pw.println("mIsTablet=" + mIsTablet);
         pw.println("mNavMode=" + mNavMode);
         for (int i = 0; i < mNavigationBars.size(); i++) {
             if (i > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index be93550..c70cce9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -183,7 +183,7 @@
     }
 
     fun getRestoredTilePosition(tile: String): Int =
-        restoredTiles?.get(tile)?.index ?: QSTileHost.POSITION_AT_END
+        restoredTiles?.get(tile)?.index ?: QSHost.POSITION_AT_END
 
     /**
      * Returns `true` if the tile has been auto-added before
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 979884c..a7aac5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -135,7 +135,7 @@
     private int mNumQuickTiles;
     private int mLastQQSTileHeight;
     private float mLastPosition;
-    private final QSTileHost mHost;
+    private final QSHost mHost;
     private final Executor mExecutor;
     private boolean mShowCollapsedOnKeyguard;
     private int mQQSTop;
@@ -146,7 +146,7 @@
     @Inject
     public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
             QSPanelController qsPanelController,
-            QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
+            QuickQSPanelController quickQSPanelController, QSHost qsTileHost,
             @Main Executor executor, TunerService tunerService,
             QSExpansionPathInterpolator qsExpansionPathInterpolator) {
         mQs = qs;
@@ -485,7 +485,7 @@
         if (specs.isEmpty()) {
             // specs should not be empty in a valid secondary page, as we scrolled to it.
             // We may crash later on because there's a null animator.
-            specs = mQsPanelController.getHost().mTileSpecs;
+            specs = mHost.getSpecs();
             Log.e(TAG, "Trying to create animators for empty page " + page + ". Tiles: " + specs);
             // return null;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 1da30ad..a71e6dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -14,15 +14,48 @@
 
 package com.android.systemui.qs;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.provider.Settings;
 
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.util.leak.GarbageMonitor;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 
 public interface QSHost {
+    String TILES_SETTING = Settings.Secure.QS_TILES;
+    int POSITION_AT_END = -1;
+
+    /**
+     * Returns the default QS tiles for the context.
+     * @param context the context to obtain the resources from
+     * @return a list of specs of the default tiles
+     */
+    static List<String> getDefaultSpecs(Context context) {
+        final ArrayList<String> tiles = new ArrayList();
+
+        final Resources res = context.getResources();
+        final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
+
+        tiles.addAll(Arrays.asList(defaultTileList.split(",")));
+        if (Build.IS_DEBUGGABLE
+                && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
+            tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
+        }
+        return tiles;
+    }
+
     void warn(String message, Throwable t);
     void collapsePanels();
     void forceCollapsePanels();
@@ -37,6 +70,44 @@
     void removeTile(String tileSpec);
     void removeTiles(Collection<String> specs);
 
+    List<String> getSpecs();
+    /**
+     * Create a view for a tile, iterating over all possible {@link QSFactory}.
+     *
+     * @see QSFactory#createTileView
+     */
+    QSTileView createTileView(Context themedContext, QSTile tile, boolean collapsedView);
+    /** Create a {@link QSTile} of a {@code tileSpec} type. */
+    QSTile createTile(String tileSpec);
+
+    /**
+     * Add a tile to the end
+     *
+     * @param spec string matching a pre-defined tilespec
+     */
+    void addTile(String spec);
+
+    /**
+     * Add a tile into the requested spot, or at the end if the position is greater than the number
+     * of tiles.
+     * @param spec string matching a pre-defined tilespec
+     * @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
+     */
+    void addTile(String spec, int requestPosition);
+    void addTile(ComponentName tile);
+
+    /**
+     * Adds a custom tile to the set of current tiles.
+     * @param tile the component name of the {@link android.service.quicksettings.TileService}
+     * @param end if true, the tile will be added at the end. If false, at the beginning.
+     */
+    void addTile(ComponentName tile, boolean end);
+    void removeTileByUser(ComponentName tile);
+    void changeTilesByUser(List<String> previousTiles, List<String> newTiles);
+
+    boolean isTileAdded(ComponentName componentName, int userId);
+    void setTileAdded(ComponentName componentName, int userId, boolean added);
+
     int indexOf(String tileSpec);
 
     InstanceId getNewInstanceId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index a4f0bdf..b476521 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -79,7 +79,6 @@
     protected boolean mExpanded;
     protected boolean mListening;
 
-    @Nullable protected QSTileHost mHost;
     private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
             new ArrayList<>();
 
@@ -359,11 +358,6 @@
         }
     }
 
-    @Nullable
-    public QSTileHost getHost() {
-        return mHost;
-    }
-
     public void updateResources() {
         updatePadding();
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index b36d0fa..83b373d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -71,7 +71,7 @@
 
     @Inject
     QSPanelController(QSPanel view, TunerService tunerService,
-            QSTileHost qstileHost, QSCustomizerController qsCustomizerController,
+            QSHost qsHost, QSCustomizerController qsCustomizerController,
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QS_PANEL) MediaHost mediaHost,
             QSTileRevealController.Factory qsTileRevealControllerFactory,
@@ -80,7 +80,7 @@
             BrightnessSliderController.Factory brightnessSliderFactory,
             FalsingManager falsingManager,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
-        super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
+        super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                 metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mTunerService = tunerService;
         mQsCustomizerController = qsCustomizerController;
@@ -172,12 +172,6 @@
         mBrightnessMirrorHandler.setController(brightnessMirrorController);
     }
 
-    /** Get the QSTileHost this panel uses. */
-    public QSTileHost getHost() {
-        return mHost;
-    }
-
-
     /** Update appearance of QSPanel. */
     public void updateResources() {
         mView.updateResources();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index bbdf6cc..2668d2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -64,7 +64,7 @@
 public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewController<T>
         implements Dumpable{
     private static final String TAG = "QSPanelControllerBase";
-    protected final QSTileHost mHost;
+    protected final QSHost mHost;
     private final QSCustomizerController mQsCustomizerController;
     private final boolean mUsingMediaPlayer;
     protected final MediaHost mMediaHost;
@@ -128,7 +128,7 @@
 
     protected QSPanelControllerBase(
             T view,
-            QSTileHost host,
+            QSHost host,
             QSCustomizerController qsCustomizerController,
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             MediaHost mediaHost,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 98af9df..0ead979 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings.Secure;
@@ -56,17 +55,14 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.settings.SecureSettings;
 
 import org.jetbrains.annotations.NotNull;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -94,16 +90,13 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int MAX_QS_INSTANCE_ID = 1 << 20;
 
-    public static final int POSITION_AT_END = -1;
-    public static final String TILES_SETTING = Secure.QS_TILES;
-
     // Shared prefs that hold tile lifecycle info.
     @VisibleForTesting
     static final String TILES = "tiles_prefs";
 
     private final Context mContext;
     private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
-    protected final ArrayList<String> mTileSpecs = new ArrayList<>();
+    private final ArrayList<String> mTileSpecs = new ArrayList<>();
     private final TunerService mTunerService;
     private final PluginManager mPluginManager;
     private final DumpManager mDumpManager;
@@ -117,7 +110,6 @@
     private final List<Callback> mCallbacks = new ArrayList<>();
     @Nullable
     private AutoTileManager mAutoTiles;
-    private final StatusBarIconController mIconController;
     private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
     private int mCurrentUser;
     private final Optional<CentralSurfaces> mCentralSurfacesOptional;
@@ -135,7 +127,6 @@
 
     @Inject
     public QSTileHost(Context context,
-            StatusBarIconController iconController,
             QSFactory defaultFactory,
             @Main Executor mainExecutor,
             PluginManager pluginManager,
@@ -152,7 +143,6 @@
             TileLifecycleManager.Factory tileLifecycleManagerFactory,
             UserFileManager userFileManager
     ) {
-        mIconController = iconController;
         mContext = context;
         mUserContext = context;
         mTunerService = tunerService;
@@ -186,10 +176,6 @@
         });
     }
 
-    public StatusBarIconController getIconController() {
-        return mIconController;
-    }
-
     @Override
     public InstanceId getNewInstanceId() {
         return mInstanceIdSequence.newInstanceId();
@@ -438,12 +424,7 @@
         addTile(spec, POSITION_AT_END);
     }
 
-    /**
-     * Add a tile into the requested spot, or at the end if the position is greater than the number
-     * of tiles.
-     * @param spec string matching a pre-defined tilespec
-     * @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
-     */
+    @Override
     public void addTile(String spec, int requestPosition) {
         mMainExecutor.execute(() ->
                 changeTileSpecs(tileSpecs -> {
@@ -483,15 +464,12 @@
         }
     }
 
+    @Override
     public void addTile(ComponentName tile) {
         addTile(tile, /* end */ false);
     }
 
-    /**
-     * Adds a custom tile to the set of current tiles.
-     * @param tile the component name of the {@link android.service.quicksettings.TileService}
-     * @param end if true, the tile will be added at the end. If false, at the beginning.
-     */
+    @Override
     public void addTile(ComponentName tile, boolean end) {
         String spec = CustomTile.toSpec(tile);
         addTile(spec, end ? POSITION_AT_END : 0);
@@ -501,6 +479,7 @@
      * This will call through {@link #changeTilesByUser}. It should only be used when a tile is
      * removed by a <b>user action</b> like {@code adb}.
      */
+    @Override
     public void removeTileByUser(ComponentName tile) {
         mMainExecutor.execute(() -> {
             List<String> newSpecs = new ArrayList<>(mTileSpecs);
@@ -519,6 +498,7 @@
      * that are removed.
      */
     @MainThread
+    @Override
     public void changeTilesByUser(List<String> previousTiles, List<String> newTiles) {
         final List<String> copy = new ArrayList<>(previousTiles);
         final int NP = copy.size();
@@ -542,8 +522,8 @@
         saveTilesToSettings(newTiles);
     }
 
-    /** Create a {@link QSTile} of a {@code tileSpec} type. */
     @Nullable
+    @Override
     public QSTile createTile(String tileSpec) {
         for (int i = 0; i < mQsFactories.size(); i++) {
             QSTile t = mQsFactories.get(i).createTile(tileSpec);
@@ -554,11 +534,7 @@
         return null;
     }
 
-    /**
-     * Create a view for a tile, iterating over all possible {@link QSFactory}.
-     *
-     * @see QSFactory#createTileView
-     */
+    @Override
     public QSTileView createTileView(Context themedContext, QSTile tile, boolean collapsedView) {
         for (int i = 0; i < mQsFactories.size(); i++) {
             QSTileView view = mQsFactories.get(i)
@@ -578,6 +554,7 @@
      *                      tile.
      * @param userId the user to check
      */
+    @Override
     public boolean isTileAdded(ComponentName componentName, int userId) {
         return mUserFileManager
                 .getSharedPreferences(TILES, 0, userId)
@@ -593,6 +570,7 @@
      * @param userId the user for this tile
      * @param added {@code true} if the tile is being added, {@code false} otherwise
      */
+    @Override
     public void setTileAdded(ComponentName componentName, int userId, boolean added) {
         mUserFileManager.getSharedPreferences(TILES, 0, userId)
                 .edit()
@@ -600,6 +578,11 @@
                 .apply();
     }
 
+    @Override
+    public List<String> getSpecs() {
+        return mTileSpecs;
+    }
+
     protected static List<String> loadTileSpecs(Context context, String tileList) {
         final Resources res = context.getResources();
 
@@ -617,7 +600,7 @@
             if (tile.isEmpty()) continue;
             if (tile.equals("default")) {
                 if (!addedDefault) {
-                    List<String> defaultSpecs = getDefaultSpecs(context);
+                    List<String> defaultSpecs = QSHost.getDefaultSpecs(context);
                     for (String spec : defaultSpecs) {
                         if (!addedSpecs.contains(spec)) {
                             tiles.add(spec);
@@ -650,25 +633,6 @@
         return tiles;
     }
 
-    /**
-     * Returns the default QS tiles for the context.
-     * @param context the context to obtain the resources from
-     * @return a list of specs of the default tiles
-     */
-    public static List<String> getDefaultSpecs(Context context) {
-        final ArrayList<String> tiles = new ArrayList<String>();
-
-        final Resources res = context.getResources();
-        final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
-
-        tiles.addAll(Arrays.asList(defaultTileList.split(",")));
-        if (Build.IS_DEBUGGABLE
-                && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
-            tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
-        }
-        return tiles;
-    }
-
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println("QSTileHost:");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 6aabe3b..2d54313 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -48,7 +48,7 @@
     private final Provider<Boolean> mUsingCollapsedLandscapeMediaProvider;
 
     @Inject
-    QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
+    QuickQSPanelController(QuickQSPanel view, QSHost qsHost,
             QSCustomizerController qsCustomizerController,
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
@@ -57,7 +57,7 @@
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
             DumpManager dumpManager
     ) {
-        super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
+        super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager);
         mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 9739011..a319fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -40,7 +40,7 @@
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSEditEvent;
 import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -57,7 +57,7 @@
 @QSScope
 public class QSCustomizerController extends ViewController<QSCustomizer> {
     private final TileQueryHelper mTileQueryHelper;
-    private final QSTileHost mQsTileHost;
+    private final QSHost mQsHost;
     private final TileAdapter mTileAdapter;
     private final ScreenLifecycle mScreenLifecycle;
     private final KeyguardStateController mKeyguardStateController;
@@ -104,12 +104,12 @@
 
     @Inject
     protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,
-            QSTileHost qsTileHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
+            QSHost qsHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
             KeyguardStateController keyguardStateController, LightBarController lightBarController,
             ConfigurationController configurationController, UiEventLogger uiEventLogger) {
         super(view);
         mTileQueryHelper = tileQueryHelper;
-        mQsTileHost = qsTileHost;
+        mQsHost = qsHost;
         mTileAdapter = tileAdapter;
         mScreenLifecycle = screenLifecycle;
         mKeyguardStateController = keyguardStateController;
@@ -175,7 +175,7 @@
 
 
     private void reset() {
-        mTileAdapter.resetTileSpecs(QSTileHost.getDefaultSpecs(getContext()));
+        mTileAdapter.resetTileSpecs(QSHost.getDefaultSpecs(getContext()));
     }
 
     public boolean isCustomizing() {
@@ -192,7 +192,7 @@
                 mView.show(x, y, mTileAdapter);
                 mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);
             }
-            mTileQueryHelper.queryTiles(mQsTileHost);
+            mTileQueryHelper.queryTiles(mQsHost);
             mKeyguardStateController.addCallback(mKeyguardCallback);
             mView.updateNavColors(mLightBarController);
         }
@@ -258,13 +258,13 @@
 
     private void save() {
         if (mTileQueryHelper.isFinished()) {
-            mTileAdapter.saveSpecs(mQsTileHost);
+            mTileAdapter.saveSpecs(mQsHost);
         }
     }
 
     private void setTileSpecs() {
         List<String> specs = new ArrayList<>();
-        for (QSTile tile : mQsTileHost.getTiles()) {
+        for (QSTile tile : mQsHost.getTiles()) {
             specs.add(tile.getTileSpec());
         }
         mTileAdapter.setTileSpecs(specs);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index d84b12c..6a05684 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -45,7 +45,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSEditEvent;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.customize.TileAdapter.Holder;
 import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
 import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
@@ -91,7 +91,7 @@
     private ItemDecoration mDecoration;
     private final MarginTileDecoration mMarginDecoration;
     private final int mMinNumTiles;
-    private final QSTileHost mHost;
+    private final QSHost mHost;
     private int mEditIndex;
     private int mTileDividerIndex;
     private int mFocusIndex;
@@ -117,7 +117,7 @@
     @Inject
     public TileAdapter(
             @QSThemedContext Context context,
-            QSTileHost qsHost,
+            QSHost qsHost,
             UiEventLogger uiEventLogger) {
         mContext = context;
         mHost = qsHost;
@@ -176,7 +176,7 @@
         mMarginDecoration.setHalfMargin(halfMargin);
     }
 
-    public void saveSpecs(QSTileHost host) {
+    public void saveSpecs(QSHost host) {
         List<String> newSpecs = new ArrayList<>();
         clearAccessibilityState();
         for (int i = 1; i < mTiles.size() && mTiles.get(i) != null; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 32a7916..d9f4484 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -38,7 +38,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
@@ -85,7 +85,7 @@
         mListener = listener;
     }
 
-    public void queryTiles(QSTileHost host) {
+    public void queryTiles(QSHost host) {
         mTiles.clear();
         mSpecs.clear();
         mFinished = false;
@@ -97,7 +97,7 @@
         return mFinished;
     }
 
-    private void addCurrentAndStockTiles(QSTileHost host) {
+    private void addCurrentAndStockTiles(QSHost host) {
         String stock = mContext.getString(R.string.quick_settings_tiles_stock);
         String current = Settings.Secure.getString(mContext.getContentResolver(),
                 Settings.Secure.QS_TILES);
@@ -153,14 +153,14 @@
     private class TileCollector implements QSTile.Callback {
 
         private final List<TilePair> mQSTileList = new ArrayList<>();
-        private final QSTileHost mQSTileHost;
+        private final QSHost mQSHost;
 
-        TileCollector(List<QSTile> tilesToAdd, QSTileHost host) {
+        TileCollector(List<QSTile> tilesToAdd, QSHost host) {
             for (QSTile tile: tilesToAdd) {
                 TilePair pair = new TilePair(tile);
                 mQSTileList.add(pair);
             }
-            mQSTileHost = host;
+            mQSHost = host;
             if (tilesToAdd.isEmpty()) {
                 mBgExecutor.execute(this::finished);
             }
@@ -168,7 +168,7 @@
 
         private void finished() {
             notifyTilesChanged(false);
-            addPackageTiles(mQSTileHost);
+            addPackageTiles(mQSHost);
         }
 
         private void startListening() {
@@ -207,7 +207,7 @@
         }
     }
 
-    private void addPackageTiles(final QSTileHost host) {
+    private void addPackageTiles(final QSHost host) {
         mBgExecutor.execute(() -> {
             Collection<QSTile> params = host.getTiles();
             PackageManager pm = mContext.getPackageManager();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 27ae171..431d6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -67,7 +67,7 @@
     static AutoTileManager provideAutoTileManager(
             Context context,
             AutoAddTracker.Builder autoAddTrackerBuilder,
-            QSTileHost host,
+            QSHost host,
             @Background Handler handler,
             SecureSettings secureSettings,
             HotspotController hotspotController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
index 237b66e..d9e5580 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
@@ -25,13 +25,13 @@
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.internal.statusbar.IAddTileResultCallback
+import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.R
-import com.android.systemui.statusbar.CommandQueue
 import java.io.PrintWriter
 import java.util.concurrent.atomic.AtomicBoolean
 import java.util.function.Consumer
@@ -40,14 +40,14 @@
 private const val TAG = "TileServiceRequestController"
 
 /**
- * Controller to interface between [TileRequestDialog] and [QSTileHost].
+ * Controller to interface between [TileRequestDialog] and [QSHost].
  */
 class TileServiceRequestController constructor(
-    private val qsTileHost: QSTileHost,
+    private val qsHost: QSHost,
     private val commandQueue: CommandQueue,
     private val commandRegistry: CommandRegistry,
     private val eventLogger: TileRequestDialogEventLogger,
-    private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsTileHost.context) }
+    private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsHost.context) }
 ) {
 
     companion object {
@@ -93,7 +93,7 @@
     }
 
     private fun addTile(componentName: ComponentName) {
-        qsTileHost.addTile(componentName, true)
+        qsHost.addTile(componentName, true)
     }
 
     @VisibleForTesting
@@ -158,7 +158,7 @@
 
     private fun isTileAlreadyAdded(componentName: ComponentName): Boolean {
         val spec = CustomTile.toSpec(componentName)
-        return qsTileHost.indexOf(spec) != -1
+        return qsHost.indexOf(spec) != -1
     }
 
     inner class TileServiceRequestCommand : Command {
@@ -194,13 +194,13 @@
         private val commandQueue: CommandQueue,
         private val commandRegistry: CommandRegistry
     ) {
-        fun create(qsTileHost: QSTileHost): TileServiceRequestController {
+        fun create(qsHost: QSHost): TileServiceRequestController {
             return TileServiceRequestController(
-                    qsTileHost,
+                    qsHost,
                     commandQueue,
                     commandRegistry,
                     TileRequestDialogEventLogger()
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 84a18d8..adc7165 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -39,7 +39,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -68,22 +68,24 @@
     private final Context mContext;
     private final Handler mMainHandler;
     private final Provider<Handler> mHandlerProvider;
-    private final QSTileHost mHost;
+    private final QSHost mHost;
     private final KeyguardStateController mKeyguardStateController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final CommandQueue mCommandQueue;
     private final UserTracker mUserTracker;
+    private final StatusBarIconController mStatusBarIconController;
 
     private int mMaxBound = DEFAULT_MAX_BOUND;
 
     @Inject
     public TileServices(
-            QSTileHost host,
+            QSHost host,
             @Main Provider<Handler> handlerProvider,
             BroadcastDispatcher broadcastDispatcher,
             UserTracker userTracker,
             KeyguardStateController keyguardStateController,
-            CommandQueue commandQueue) {
+            CommandQueue commandQueue,
+            StatusBarIconController statusBarIconController) {
         mHost = host;
         mKeyguardStateController = keyguardStateController;
         mContext = mHost.getContext();
@@ -92,6 +94,7 @@
         mMainHandler = mHandlerProvider.get();
         mUserTracker = userTracker;
         mCommandQueue = commandQueue;
+        mStatusBarIconController = statusBarIconController;
         mCommandQueue.addCallback(mRequestListeningCallback);
     }
 
@@ -99,7 +102,7 @@
         return mContext;
     }
 
-    public QSTileHost getHost() {
+    public QSHost getHost() {
         return mHost;
     }
 
@@ -131,8 +134,7 @@
             mTiles.remove(tile.getComponent());
             final String slot = tile.getComponent().getClassName();
             // TileServices doesn't know how to add more than 1 icon per slot, so remove all
-            mMainHandler.post(() -> mHost.getIconController()
-                    .removeAllIconsForExternalSlot(slot));
+            mMainHandler.post(() -> mStatusBarIconController.removeAllIconsForSlot(slot));
         }
     }
 
@@ -309,7 +311,7 @@
                     mMainHandler.post(new Runnable() {
                         @Override
                         public void run() {
-                            StatusBarIconController iconController = mHost.getIconController();
+                            StatusBarIconController iconController = mStatusBarIconController;
                             iconController.setIcon(componentName.getClassName(), statusIcon);
                             iconController.setExternalIcon(componentName.getClassName());
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 346acf9..645b125 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.recents;
 
-import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -265,7 +265,7 @@
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
             if (!QuickStepContract.isGesturalMode(mNavBarMode)
-            	    && hasSoftNavigationBar(mContext.getDisplayId()) && !isLargeScreen(mContext)) {
+            	    && hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f4cd985..51c5183 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -213,7 +213,8 @@
             DeviceProvisionedController deviceProvisionedController,
             KeyguardStateController keyguardStateController,
             SecureSettings secureSettings,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            LockPatternUtils lockPatternUtils) {
         mContext = context;
         mMainHandler = mainHandler;
         mDevicePolicyManager = devicePolicyManager;
@@ -225,7 +226,7 @@
         mClickNotifier = clickNotifier;
         mOverviewProxyServiceLazy = overviewProxyServiceLazy;
         statusBarStateController.addCallback(this);
-        mLockPatternUtils = new LockPatternUtils(context);
+        mLockPatternUtils = lockPatternUtils;
         mKeyguardManager = keyguardManager;
         mBroadcastDispatcher = broadcastDispatcher;
         mDeviceProvisionedController = deviceProvisionedController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index f0b221d..0de3246 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -344,7 +344,7 @@
             or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
 }
 
-class ChannelEditorDialog(context: Context) : Dialog(context) {
+class ChannelEditorDialog(context: Context, theme: Int) : Dialog(context, theme) {
     fun updateDoneButtonText(hasChanges: Boolean) {
         findViewById<TextView>(R.id.done_button)?.setText(
                 if (hasChanges)
@@ -361,7 +361,7 @@
         }
 
         fun build(): ChannelEditorDialog {
-            return ChannelEditorDialog(context)
+            return ChannelEditorDialog(context, R.style.Theme_SystemUI_Dialog)
         }
     }
 }
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 1c91b9f..2affa77 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
@@ -3729,7 +3729,9 @@
             }
             pw.println("Roundness: " + getRoundableState().debugString());
 
-            if (mIsSummaryWithChildren) {
+            int transientViewCount = mChildrenContainer == null
+                    ? 0 : mChildrenContainer.getTransientViewCount();
+            if (mIsSummaryWithChildren || transientViewCount > 0) {
                 pw.println();
                 pw.print("ChildrenContainer");
                 pw.print(" visibility: " + mChildrenContainer.getVisibility());
@@ -3737,8 +3739,7 @@
                 pw.print(", translationY: " + mChildrenContainer.getTranslationY());
                 pw.println();
                 List<ExpandableNotificationRow> notificationChildren = getAttachedChildren();
-                pw.println("Children: " + notificationChildren.size());
-                pw.print("{");
+                pw.print("Children: " + notificationChildren.size() + " {");
                 pw.increaseIndent();
                 for (ExpandableNotificationRow child : notificationChildren) {
                     pw.println();
@@ -3746,6 +3747,15 @@
                 }
                 pw.decreaseIndent();
                 pw.println("}");
+                pw.print("Transient Views: " + transientViewCount + " {");
+                pw.increaseIndent();
+                for (int i = 0; i < transientViewCount; i++) {
+                    pw.println();
+                    ExpandableView child = (ExpandableView) mChildrenContainer.getTransientView(i);
+                    child.dump(pw, args);
+                }
+                pw.decreaseIndent();
+                pw.println("}");
             } else if (mPrivateLayout != null) {
                 mPrivateLayout.dumpSmartReplies(pw);
             }
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 f1694ac..2dda6fd 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
@@ -317,7 +317,7 @@
         }
         mView.removeChildNotification(childView);
         if (!isTransfer) {
-            mListContainer.notifyGroupChildRemoved(childView, mView);
+            mListContainer.notifyGroupChildRemoved(childView, mView.getChildrenContainer());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index d2087ba6..e09b94b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -2792,7 +2792,7 @@
         }
         child.setOnHeightChangedListener(null);
         updateScrollStateForRemovedChild(child);
-        boolean animationGenerated = generateRemoveAnimation(child);
+        boolean animationGenerated = container != null && generateRemoveAnimation(child);
         if (animationGenerated) {
             if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
                 container.addTransientView(child, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 576df7a..f6d53b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -31,7 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.qs.SettingObserver;
 import com.android.systemui.qs.external.CustomTile;
@@ -75,7 +75,7 @@
     private final String mSafetySpec;
 
     protected final Context mContext;
-    protected final QSTileHost mHost;
+    protected final QSHost mHost;
     protected final Handler mHandler;
     protected final SecureSettings mSecureSettings;
     protected final AutoAddTracker mAutoTracker;
@@ -92,7 +92,7 @@
     private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
 
     public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
-            QSTileHost host,
+            QSHost host,
             @Background Handler handler,
             SecureSettings secureSettings,
             HotspotController hotspotController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 6f4afe4..b8c7a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -50,6 +50,7 @@
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSPanelController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
@@ -103,6 +104,7 @@
     private final SystemBarAttributesListener mSystemBarAttributesListener;
     private final Lazy<CameraLauncher> mCameraLauncherLazy;
     private final QuickSettingsController mQsController;
+    private final QSHost mQSHost;
 
     private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -135,7 +137,8 @@
             @DisplayId int displayId,
             SystemBarAttributesListener systemBarAttributesListener,
             Lazy<CameraLauncher> cameraLauncherLazy,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            QSHost qsHost) {
         mCentralSurfaces = centralSurfaces;
         mQsController = quickSettingsController;
         mContext = context;
@@ -161,6 +164,7 @@
         mDisplayId = displayId;
         mCameraLauncherLazy = cameraLauncherLazy;
         mUserTracker = userTracker;
+        mQSHost = qsHost;
 
         mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
         mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -181,22 +185,17 @@
 
     @Override
     public void addQsTile(ComponentName tile) {
-        QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
-        if (qsPanelController != null && qsPanelController.getHost() != null) {
-            qsPanelController.getHost().addTile(tile);
-        }
+        mQSHost.addTile(tile);
     }
 
     @Override
     public void remQsTile(ComponentName tile) {
-        QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
-        if (qsPanelController != null && qsPanelController.getHost() != null) {
-            qsPanelController.getHost().removeTileByUser(tile);
-        }
+        mQSHost.removeTileByUser(tile);
     }
 
     @Override
     public void clickTile(ComponentName tile) {
+        // Can't inject this because it changes with the QS fragment
         QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
         if (qsPanelController != null) {
             qsPanelController.clickTile(tile);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index b23d870..8cfe2ea 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -42,7 +42,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -68,7 +68,7 @@
     // Things that use the tunable infrastructure but are now real user settings and
     // shouldn't be reset with tuner settings.
     private static final String[] RESET_EXCEPTION_LIST = new String[] {
-            QSTileHost.TILES_SETTING,
+            QSHost.TILES_SETTING,
             Settings.Secure.DOZE_ALWAYS_ON,
             Settings.Secure.MEDIA_CONTROLS_RESUME,
             Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
new file mode 100644
index 0000000..30fed0b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.app.ActivityTaskManager
+import android.content.pm.PackageManager
+import android.os.PowerManager
+import android.telecom.TelecomManager
+import android.telephony.TelephonyManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class EmergencyButtonControllerTest : SysuiTestCase() {
+    lateinit var underTest: EmergencyButtonController
+    @Mock lateinit var emergencyButton: EmergencyButton
+    @Mock lateinit var configurationController: ConfigurationController
+    @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock lateinit var telephonyManager: TelephonyManager
+    @Mock lateinit var powerManager: PowerManager
+    @Mock lateinit var activityTaskManager: ActivityTaskManager
+    @Mock lateinit var shadeController: ShadeController
+    @Mock lateinit var telecomManager: TelecomManager
+    @Mock lateinit var metricsLogger: MetricsLogger
+    @Mock lateinit var lockPatternUtils: LockPatternUtils
+    @Mock lateinit var packageManager: PackageManager
+    val fakeSystemClock = FakeSystemClock()
+    val mainExecutor = FakeExecutor(fakeSystemClock)
+    val backgroundExecutor = FakeExecutor(fakeSystemClock)
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            EmergencyButtonController(
+                emergencyButton,
+                configurationController,
+                keyguardUpdateMonitor,
+                telephonyManager,
+                powerManager,
+                activityTaskManager,
+                shadeController,
+                telecomManager,
+                metricsLogger,
+                lockPatternUtils,
+                mainExecutor,
+                backgroundExecutor
+            )
+        context.setMockPackageManager(packageManager)
+        Mockito.`when`(emergencyButton.context).thenReturn(context)
+    }
+
+    @Test
+    fun testUpdateEmergencyButton() {
+        Mockito.`when`(telecomManager.isInCall).thenReturn(true)
+        Mockito.`when`(lockPatternUtils.isSecure(anyInt())).thenReturn(true)
+        Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
+            .thenReturn(true)
+        underTest.updateEmergencyCallButton()
+        backgroundExecutor.runAllReady()
+        verify(emergencyButton, never())
+            .updateEmergencyCallButton(
+                /* isInCall= */ any(),
+                /* hasTelephonyRadio= */ any(),
+                /* simLocked= */ any(),
+                /* isSecure= */ any()
+            )
+        mainExecutor.runAllReady()
+        verify(emergencyButton)
+            .updateEmergencyCallButton(
+                /* isInCall= */ eq(true),
+                /* hasTelephonyRadio= */ eq(true),
+                /* simLocked= */ any(),
+                /* isSecure= */ eq(true)
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index a1e4f70..395eb8f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -615,9 +615,8 @@
         mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
         mTestableLooper.processAllMessages();
 
-        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
-                anyInt());
-        verify(mFingerprintManager, never()).detectFingerprint(any(), any(), any());
+        verifyFingerprintAuthenticateCall();
+        verifyFingerprintDetectNeverCalled();
     }
 
     @Test
@@ -627,9 +626,8 @@
         mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
         mTestableLooper.processAllMessages();
 
-        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
-                anyInt(), anyInt());
-        verify(mFingerprintManager, never()).detectFingerprint(any(), any(), any());
+        verifyFingerprintAuthenticateNeverCalled();
+        verifyFingerprintDetectNeverCalled();
     }
 
     @Test
@@ -643,8 +641,8 @@
         mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
         mTestableLooper.processAllMessages();
 
-        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt());
-        verify(mFingerprintManager).detectFingerprint(any(), any(), any());
+        verifyFingerprintAuthenticateNeverCalled();
+        verifyFingerprintDetectCall();
     }
 
     @Test
@@ -732,9 +730,7 @@
     @Test
     public void testTriesToAuthenticate_whenBouncer() {
         setKeyguardBouncerVisibility(true);
-
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
-        verify(mFaceManager, never()).hasEnrolledTemplates(anyInt());
+        verifyFaceAuthenticateCall();
     }
 
     @Test
@@ -742,7 +738,7 @@
         mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(
                 /* bouncerIsOrWillBeShowing */ true, /* bouncerFullyShown */ false);
 
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -750,7 +746,8 @@
         keyguardIsVisible();
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+
+        verifyFaceAuthenticateCall();
         verify(mUiEventLogger).logWithInstanceIdAndPosition(
                 eq(FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP),
                 eq(0),
@@ -766,7 +763,7 @@
         mTestableLooper.processAllMessages();
 
         keyguardIsVisible();
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -777,7 +774,8 @@
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -801,8 +799,8 @@
         mTestableLooper.processAllMessages();
 
         // THEN face detect and authenticate are NOT triggered
-        verify(mFaceManager, never()).detectFace(any(), any(), any());
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceDetectNeverCalled();
+        verifyFaceAuthenticateNeverCalled();
 
         // THEN biometric help message sent to callback
         verify(keyguardUpdateMonitorCallback).onBiometricHelp(
@@ -823,8 +821,8 @@
         mTestableLooper.processAllMessages();
 
         // FACE detect is triggered, not authenticate
-        verify(mFaceManager).detectFace(any(), any(), any());
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceDetectCall();
+        verifyFaceAuthenticateNeverCalled();
 
         // WHEN bouncer becomes visible
         setKeyguardBouncerVisibility(true);
@@ -832,8 +830,8 @@
 
         // THEN face scanning is not run
         mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
-        verify(mFaceManager, never()).detectFace(any(), any(), any());
+        verifyFaceAuthenticateNeverCalled();
+        verifyFaceDetectNeverCalled();
     }
 
     @Test
@@ -848,8 +846,8 @@
         mTestableLooper.processAllMessages();
 
         // FACE detect and authenticate are NOT triggered
-        verify(mFaceManager, never()).detectFace(any(), any(), any());
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceDetectNeverCalled();
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -887,7 +885,7 @@
         mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
         mKeyguardUpdateMonitor.setAssistantVisible(true);
 
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateCall();
     }
 
     @Test
@@ -895,11 +893,7 @@
         mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
         mKeyguardUpdateMonitor.setAssistantVisible(true);
 
-        verify(mFaceManager, never()).authenticate(any(),
-                any(),
-                any(),
-                any(),
-                anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -910,15 +904,12 @@
 
         // THEN fingerprint shouldn't listen
         assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
-        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
-                anyInt(), anyInt());
-
+        verifyFingerprintAuthenticateNeverCalled();
         // WHEN alternate bouncer is shown
         mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
 
         // THEN make sure FP listening begins
-        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
-                anyInt());
+        verifyFingerprintAuthenticateCall();
     }
 
     @Test
@@ -930,7 +921,7 @@
                 KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
                 new ArrayList<>());
         keyguardIsVisible();
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateCall();
     }
 
     @Test
@@ -938,7 +929,7 @@
         mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
         mKeyguardUpdateMonitor.setAssistantVisible(true);
 
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateCall();
         mTestableLooper.processAllMessages();
         clearInvocations(mFaceManager);
 
@@ -951,11 +942,7 @@
         mKeyguardUpdateMonitor.handleKeyguardReset();
 
         assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
-        verify(mFaceManager, never()).authenticate(any(),
-                any(),
-                any(),
-                any(),
-                anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -965,7 +952,7 @@
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
                 KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
         keyguardIsVisible();
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -977,8 +964,8 @@
         keyguardIsVisible();
         mTestableLooper.processAllMessages();
 
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
-        verify(mFaceManager, never()).detectFace(any(), any(), any());
+        verifyFaceAuthenticateNeverCalled();
+        verifyFaceDetectNeverCalled();
     }
 
     @Test
@@ -987,16 +974,14 @@
                 .onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "");
 
         // THEN doesn't authenticate immediately
-        verify(mFingerprintManager, never()).authenticate(any(),
-                any(), any(), any(), anyInt(), anyInt(), anyInt());
+        verifyFingerprintAuthenticateNeverCalled();
 
         // WHEN all messages (with delays) are processed
         mTestableLooper.moveTimeForward(HAL_POWER_PRESS_TIMEOUT);
         mTestableLooper.processAllMessages();
 
         // THEN fingerprint manager attempts to authenticate again
-        verify(mFingerprintManager).authenticate(any(),
-                any(), any(), any(), anyInt(), anyInt(), anyInt());
+        verifyFingerprintAuthenticateCall();
     }
 
     @Test
@@ -1008,7 +993,7 @@
         setKeyguardBouncerVisibility(true);
         mTestableLooper.processAllMessages();
 
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -1135,9 +1120,8 @@
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
-        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
-                anyInt());
+        verifyFaceAuthenticateCall();
+        verifyFingerprintAuthenticateCall();
 
         when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
                 .thenReturn(fingerprintLockoutMode);
@@ -1585,9 +1569,7 @@
     public void testFaceDoesNotAuth_afterPinAttempt() {
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.setCredentialAttempted();
-        verify(mFingerprintManager, never()).authenticate(any(), any(), any(),
-                any(), anyInt());
-        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -1962,9 +1944,8 @@
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
-        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
-                anyInt());
+        verifyFaceAuthenticateCall();
+        verifyFingerprintAuthenticateCall();
 
         mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
         // Make sure keyguard is going away after face auth attempt, and that it calls
@@ -1990,8 +1971,7 @@
         mKeyguardUpdateMonitor.dispatchDreamingStopped();
         mTestableLooper.processAllMessages();
 
-        verify(mFaceManager, never()).authenticate(
-                any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
     }
 
     @Test
@@ -2004,15 +1984,14 @@
         mTestableLooper.processAllMessages();
 
         // THEN face auth isn't triggered
-        verify(mFaceManager, never()).authenticate(
-                any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateNeverCalled();
 
         // WHEN device wakes up from the power button
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
 
         // THEN face auth is triggered
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateCall();
     }
 
     @Test
@@ -2182,7 +2161,7 @@
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateCall();
         verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
                 anyInt());
 
@@ -2215,7 +2194,7 @@
         mTestableLooper.processAllMessages();
         keyguardIsVisible();
 
-        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+        verifyFaceAuthenticateCall();
 
         final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
         mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
@@ -2486,6 +2465,40 @@
                 eq(false));
     }
 
+    private void verifyFingerprintAuthenticateNeverCalled() {
+        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyInt(), anyInt());
+    }
+
+    private void verifyFingerprintAuthenticateCall() {
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                anyInt());
+    }
+
+    private void verifyFingerprintDetectNeverCalled() {
+        verify(mFingerprintManager, never()).detectFingerprint(any(), any(), any());
+    }
+
+    private void verifyFingerprintDetectCall() {
+        verify(mFingerprintManager).detectFingerprint(any(), any(), any());
+    }
+
+    private void verifyFaceAuthenticateNeverCalled() {
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
+    }
+
+    private void verifyFaceAuthenticateCall() {
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
+    }
+
+    private void verifyFaceDetectNeverCalled() {
+        verify(mFaceManager, never()).detectFace(any(), any(), any());
+    }
+
+    private void verifyFaceDetectCall() {
+        verify(mFaceManager).detectFace(any(), any(), any());
+    }
+
     private void userDeviceLockDown() {
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
         when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
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 f57b35a..6333a68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -63,6 +63,7 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -97,6 +98,7 @@
 
     @JvmField @Rule var rule = MockitoJUnit.rule()
 
+    @Mock lateinit var keyguardStateController: KeyguardStateController
     @Mock lateinit var layoutInflater: LayoutInflater
     @Mock lateinit var fingerprintManager: FingerprintManager
     @Mock lateinit var windowManager: WindowManager
@@ -136,6 +138,7 @@
         keyguardBouncerRepository = FakeKeyguardBouncerRepository()
         alternateBouncerInteractor =
             AlternateBouncerInteractor(
+                keyguardStateController,
                 keyguardBouncerRepository,
                 FakeBiometricSettingsRepository(),
                 FakeDeviceEntryFingerprintAuthRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 54c9d39..86fb279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.util.time.SystemClock
 import kotlinx.coroutines.Dispatchers
@@ -92,6 +93,7 @@
             )
         mAlternateBouncerInteractor =
             AlternateBouncerInteractor(
+                mock(KeyguardStateController::class.java),
                 keyguardBouncerRepository,
                 mock(BiometricSettingsRepository::class.java),
                 mock(DeviceEntryFingerprintAuthRepository::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
index 18e80ea..1365132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -28,6 +28,8 @@
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.util.time.SystemClock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +52,7 @@
     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
     private lateinit var deviceEntryFingerprintAuthRepository:
         FakeDeviceEntryFingerprintAuthRepository
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var systemClock: SystemClock
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var bouncerLogger: TableLogBuffer
@@ -70,6 +73,7 @@
         featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) }
         underTest =
             AlternateBouncerInteractor(
+                keyguardStateController,
                 bouncerRepository,
                 biometricSettingsRepository,
                 deviceEntryFingerprintAuthRepository,
@@ -134,6 +138,14 @@
     }
 
     @Test
+    fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() {
+        givenCanShowAlternateBouncer()
+        whenever(keyguardStateController.isUnlocked).thenReturn(true)
+
+        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
+    }
+
+    @Test
     fun show_whenCannotShow() {
         givenCannotShowAlternateBouncer()
 
@@ -163,6 +175,7 @@
         biometricSettingsRepository.setStrongBiometricAllowed(true)
         biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
         deviceEntryFingerprintAuthRepository.setLockedOut(false)
+        whenever(keyguardStateController.isUnlocked).thenReturn(false)
     }
 
     private fun givenCannotShowAlternateBouncer() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 89405c1..2212bbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -141,8 +141,8 @@
 
     @Test
     public void testCreateNavigationBarsIncludeDefaultTrue() {
-        // Large screens may be using taskbar and the logic is different
-        mNavigationBarController.mIsLargeScreen = false;
+        // Tablets may be using taskbar and the logic is different
+        mNavigationBarController.mIsTablet = false;
         doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
 
         mNavigationBarController.createNavigationBars(true, null);
@@ -290,7 +290,7 @@
     @Test
     public void testConfigurationChange_taskbarNotInitialized() {
         Configuration configuration = mContext.getResources().getConfiguration();
-        when(Utilities.isLargeScreen(any())).thenReturn(true);
+        when(Utilities.isTablet(any())).thenReturn(true);
         mNavigationBarController.onConfigChanged(configuration);
         verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration);
     }
@@ -298,7 +298,7 @@
     @Test
     public void testConfigurationChange_taskbarInitialized() {
         Configuration configuration = mContext.getResources().getConfiguration();
-        when(Utilities.isLargeScreen(any())).thenReturn(true);
+        when(Utilities.isTablet(any())).thenReturn(true);
         when(mTaskbarDelegate.isInitialized()).thenReturn(true);
         mNavigationBarController.onConfigChanged(configuration);
         verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 5058373..3d55c51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -71,7 +71,7 @@
     @Mock
     private QSPanel mQSPanel;
     @Mock
-    private QSTileHost mQSTileHost;
+    private QSHost mQSHost;
     @Mock
     private QSCustomizerController mQSCustomizerController;
     @Mock
@@ -105,7 +105,7 @@
 
     /** Implementation needed to ensure we have a reflectively-available class name. */
     private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
-        protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
+        protected TestableQSPanelControllerBase(QSPanel view, QSHost host,
                 QSCustomizerController qsCustomizerController, MediaHost mediaHost,
                 MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
                 DumpManager dumpManager) {
@@ -130,8 +130,8 @@
         when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
         when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
         when(mQSTile.getTileSpec()).thenReturn("dnd");
-        when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
-        when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
+        when(mQSHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
+        when(mQSHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
         when(mQSTileRevealControllerFactory.create(any(), any()))
                 .thenReturn(mQSTileRevealController);
         when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
@@ -142,7 +142,7 @@
             return null;
         }).when(mQSPanel).setListening(anyBoolean());
 
-        mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
+        mController = new TestableQSPanelControllerBase(mQSPanel, mQSHost,
                 mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
 
@@ -155,7 +155,7 @@
         mController.onViewDetached();
 
         QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
-                mQSTileHost, mQSCustomizerController, mMediaHost,
+                mQSHost, mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
             @Override
             protected QSTileRevealController createTileRevealController() {
@@ -250,7 +250,7 @@
 
         when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
-        mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
+        mController = new TestableQSPanelControllerBase(mQSPanel, mQSHost,
                 mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
         mController.init();
@@ -259,7 +259,7 @@
 
         when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
-        mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
+        mController = new TestableQSPanelControllerBase(mQSPanel, mQSHost,
                 mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
         mController.init();
@@ -291,7 +291,7 @@
 
     @Test
     public void testRefreshAllTilesDoesntRefreshListeningTiles() {
-        when(mQSTileHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
 
         when(mQSTile.isListening()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 5c5fbc9..a0d8f98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -40,7 +40,7 @@
 
     @Mock private lateinit var qsPanel: QSPanel
     @Mock private lateinit var tunerService: TunerService
-    @Mock private lateinit var qsTileHost: QSTileHost
+    @Mock private lateinit var qsHost: QSHost
     @Mock private lateinit var qsCustomizerController: QSCustomizerController
     @Mock private lateinit var qsTileRevealControllerFactory: QSTileRevealController.Factory
     @Mock private lateinit var dumpManager: DumpManager
@@ -79,7 +79,7 @@
         controller = QSPanelController(
             qsPanel,
             tunerService,
-            qsTileHost,
+            qsHost,
             qsCustomizerController,
             /* usingMediaPlayer= */ true,
             mediaHost,
@@ -109,7 +109,7 @@
 
     @Test
     fun testSetListeningDoesntRefreshListeningTiles() {
-        whenever(qsTileHost.getTiles()).thenReturn(listOf(tile, otherTile))
+        whenever(qsHost.getTiles()).thenReturn(listOf(tile, otherTile))
         controller.setTiles()
         whenever(tile.isListening()).thenReturn(false)
         whenever(otherTile.isListening()).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index fb1a720..34d2b14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -69,7 +69,6 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.FakeSharedPreferences;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -100,11 +99,9 @@
     private static ComponentName CUSTOM_TILE =
             ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
     private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
-    private static final String SETTING = QSTileHost.TILES_SETTING;
+    private static final String SETTING = QSHost.TILES_SETTING;
 
     @Mock
-    private StatusBarIconController mIconController;
-    @Mock
     private QSFactory mDefaultFactory;
     @Mock
     private PluginManager mPluginManager;
@@ -167,7 +164,7 @@
 
         mSecureSettings = new FakeSettings();
         saveSetting("");
-        mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mMainExecutor,
+        mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
                 mPluginManager, mTunerService, mAutoTiles, mDumpManager, mCentralSurfaces,
                 mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
                 mTileServiceRequestControllerBuilder, mTileLifecycleManagerFactory,
@@ -248,44 +245,44 @@
     public void testRemoveWifiAndCellularWithoutInternet() {
         saveSetting("wifi, spec1, cell, spec2");
 
-        assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+        assertEquals("internet", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec1", mQSTileHost.getSpecs().get(1));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
     public void testRemoveWifiAndCellularWithInternet() {
         saveSetting("wifi, spec1, cell, spec2, internet");
 
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
+        assertEquals("internet", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
     public void testRemoveWifiWithoutInternet() {
         saveSetting("spec1, wifi, spec2");
 
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("internet", mQSTileHost.getSpecs().get(1));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
     public void testRemoveCellWithInternet() {
         saveSetting("spec1, spec2, cell, internet");
 
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
+        assertEquals("internet", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
     public void testNoWifiNoCellularNoInternet() {
         saveSetting("spec1,spec2");
 
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
     }
 
     @Test
@@ -332,9 +329,9 @@
 
         mQSTileHost.addTile("spec1");
 
-        assertEquals(2, mQSTileHost.mTileSpecs.size());
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+        assertEquals(2, mQSTileHost.getSpecs().size());
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
     }
 
     @Test
@@ -346,10 +343,10 @@
         mQSTileHost.addTile("spec2", 1);
         mMainExecutor.runAllReady();
 
-        assertEquals(3, mQSTileHost.mTileSpecs.size());
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("spec3", mQSTileHost.mTileSpecs.get(2));
+        assertEquals(3, mQSTileHost.getSpecs().size());
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
+        assertEquals("spec3", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
@@ -361,10 +358,10 @@
         mQSTileHost.addTile("spec2", 100);
         mMainExecutor.runAllReady();
 
-        assertEquals(3, mQSTileHost.mTileSpecs.size());
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec3", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+        assertEquals(3, mQSTileHost.getSpecs().size());
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec3", mQSTileHost.getSpecs().get(1));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
@@ -376,10 +373,10 @@
         mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
         mMainExecutor.runAllReady();
 
-        assertEquals(3, mQSTileHost.mTileSpecs.size());
-        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
-        assertEquals("spec3", mQSTileHost.mTileSpecs.get(1));
-        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+        assertEquals(3, mQSTileHost.getSpecs().size());
+        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
+        assertEquals("spec3", mQSTileHost.getSpecs().get(1));
+        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
     }
 
     @Test
@@ -389,8 +386,8 @@
         mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
         mMainExecutor.runAllReady();
 
-        assertEquals(1, mQSTileHost.mTileSpecs.size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
+        assertEquals(1, mQSTileHost.getSpecs().size());
+        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
     }
 
     @Test
@@ -400,8 +397,8 @@
         mQSTileHost.addTile(CUSTOM_TILE);
         mMainExecutor.runAllReady();
 
-        assertEquals(2, mQSTileHost.mTileSpecs.size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
+        assertEquals(2, mQSTileHost.getSpecs().size());
+        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
     }
 
     @Test
@@ -411,8 +408,8 @@
         mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
         mMainExecutor.runAllReady();
 
-        assertEquals(2, mQSTileHost.mTileSpecs.size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
+        assertEquals(2, mQSTileHost.getSpecs().size());
+        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
     }
 
     @Test
@@ -422,8 +419,8 @@
         mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
         mMainExecutor.runAllReady();
 
-        assertEquals(2, mQSTileHost.mTileSpecs.size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(1));
+        assertEquals(2, mQSTileHost.getSpecs().size());
+        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(1));
     }
 
     @Test
@@ -478,7 +475,7 @@
         mQSTileHost.removeTiles(List.of("spec1", "spec2"));
 
         mMainExecutor.runAllReady();
-        assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec3"), mQSTileHost.getSpecs());
     }
 
     @Test
@@ -488,7 +485,7 @@
         mQSTileHost.removeTile("spec3");
 
         mMainExecutor.runAllReady();
-        assertEquals(List.of("spec2"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
         assertEquals("spec2", getSetting());
     }
 
@@ -497,10 +494,10 @@
         saveSetting("spec1,spec2");
 
         mQSTileHost.addTile("spec3");
-        assertEquals(List.of("spec1", "spec2"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
 
         mMainExecutor.runAllReady();
-        assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
     }
 
     @Test
@@ -508,10 +505,10 @@
         saveSetting("spec1,spec2");
 
         mQSTileHost.removeTile("spec1");
-        assertEquals(List.of("spec1", "spec2"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
 
         mMainExecutor.runAllReady();
-        assertEquals(List.of("spec2"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
     }
 
     @Test
@@ -519,10 +516,10 @@
         saveSetting("spec1,spec2,spec3");
 
         mQSTileHost.removeTiles(List.of("spec3", "spec1"));
-        assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
 
         mMainExecutor.runAllReady();
-        assertEquals(List.of("spec2"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
     }
 
     @Test
@@ -530,17 +527,17 @@
         saveSetting("spec1," + CUSTOM_TILE_SPEC);
 
         mQSTileHost.removeTileByUser(CUSTOM_TILE);
-        assertEquals(List.of("spec1", CUSTOM_TILE_SPEC), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1", CUSTOM_TILE_SPEC), mQSTileHost.getSpecs());
 
         mMainExecutor.runAllReady();
-        assertEquals(List.of("spec1"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
     }
 
     @Test
     public void testNonValidTileNotStoredInSettings() {
         saveSetting("spec1,not-valid");
 
-        assertEquals(List.of("spec1"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
         assertEquals("spec1", getSetting());
     }
 
@@ -548,14 +545,14 @@
     public void testNotAvailableTileNotStoredInSettings() {
         saveSetting("spec1,na");
 
-        assertEquals(List.of("spec1"), mQSTileHost.mTileSpecs);
+        assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
         assertEquals("spec1", getSetting());
     }
 
     @Test
     public void testIsTileAdded_true() {
         int user = mUserTracker.getUserId();
-        getSharedPreferenecesForUser(user)
+        getSharedPreferencesForUser(user)
                 .edit()
                 .putBoolean(CUSTOM_TILE.flattenToString(), true)
                 .apply();
@@ -566,7 +563,7 @@
     @Test
     public void testIsTileAdded_false() {
         int user = mUserTracker.getUserId();
-        getSharedPreferenecesForUser(user)
+        getSharedPreferencesForUser(user)
                 .edit()
                 .putBoolean(CUSTOM_TILE.flattenToString(), false)
                 .apply();
@@ -597,7 +594,7 @@
         int user = mUserTracker.getUserId();
         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
 
-        assertTrue(getSharedPreferenecesForUser(user)
+        assertTrue(getSharedPreferencesForUser(user)
                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
     }
 
@@ -606,7 +603,7 @@
         int user = mUserTracker.getUserId();
         mQSTileHost.setTileAdded(CUSTOM_TILE, user, false);
 
-        assertFalse(getSharedPreferenecesForUser(user)
+        assertFalse(getSharedPreferencesForUser(user)
                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
     }
 
@@ -615,7 +612,7 @@
         int user = mUserTracker.getUserId();
         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
 
-        assertFalse(getSharedPreferenecesForUser(user + 1)
+        assertFalse(getSharedPreferencesForUser(user + 1)
                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
     }
 
@@ -627,8 +624,8 @@
         // This will be done by TileServiceManager
         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
 
-        mQSTileHost.changeTilesByUser(mQSTileHost.mTileSpecs, List.of("spec1"));
-        assertFalse(getSharedPreferenecesForUser(user)
+        mQSTileHost.changeTilesByUser(mQSTileHost.getSpecs(), List.of("spec1"));
+        assertFalse(getSharedPreferencesForUser(user)
                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
     }
 
@@ -642,7 +639,7 @@
 
         mQSTileHost.removeTileByUser(CUSTOM_TILE);
         mMainExecutor.runAllReady();
-        assertFalse(getSharedPreferenecesForUser(user)
+        assertFalse(getSharedPreferencesForUser(user)
                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
     }
 
@@ -656,7 +653,7 @@
 
         mQSTileHost.removeTile(CUSTOM_TILE_SPEC);
         mMainExecutor.runAllReady();
-        assertFalse(getSharedPreferenecesForUser(user)
+        assertFalse(getSharedPreferencesForUser(user)
                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
     }
 
@@ -681,12 +678,12 @@
         assertEquals(CUSTOM_TILE.getClassName(), proto.tiles[1].getComponentName().className);
     }
 
-    private SharedPreferences getSharedPreferenecesForUser(int user) {
+    private SharedPreferences getSharedPreferencesForUser(int user) {
         return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
     }
 
     private class TestQSTileHost extends QSTileHost {
-        TestQSTileHost(Context context, StatusBarIconController iconController,
+        TestQSTileHost(Context context,
                 QSFactory defaultFactory, Executor mainExecutor,
                 PluginManager pluginManager, TunerService tunerService,
                 Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
@@ -696,7 +693,7 @@
                 TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
                 TileLifecycleManager.Factory tileLifecycleManagerFactory,
                 UserFileManager userFileManager) {
-            super(context, iconController, defaultFactory, mainExecutor, pluginManager,
+            super(context, defaultFactory, mainExecutor, pluginManager,
                     tunerService, autoTiles, dumpManager, Optional.of(centralSurfaces), qsLogger,
                     uiEventLogger, userTracker, secureSettings, customTileStatePersister,
                     tileServiceRequestControllerBuilder, tileLifecycleManagerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index f53e997..71ea831 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -50,7 +50,7 @@
 class QuickQSPanelControllerTest : SysuiTestCase() {
 
     @Mock private lateinit var quickQSPanel: QuickQSPanel
-    @Mock private lateinit var qsTileHost: QSTileHost
+    @Mock private lateinit var qsHost: QSHost
     @Mock private lateinit var qsCustomizerController: QSCustomizerController
     @Mock private lateinit var mediaHost: MediaHost
     @Mock private lateinit var metricsLogger: MetricsLogger
@@ -75,12 +75,12 @@
         whenever(quickQSPanel.isAttachedToWindow).thenReturn(true)
         whenever(quickQSPanel.dumpableTag).thenReturn("")
         whenever(quickQSPanel.resources).thenReturn(mContext.resources)
-        whenever(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
+        whenever(qsHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
 
         controller =
             TestQuickQSPanelController(
                 quickQSPanel,
-                qsTileHost,
+                qsHost,
                 qsCustomizerController,
                 /* usingMediaPlayer = */ false,
                 mediaHost,
@@ -102,7 +102,7 @@
     fun testTileSublistWithFewerTiles_noCrash() {
         whenever(quickQSPanel.numQuickTiles).thenReturn(3)
 
-        whenever(qsTileHost.tiles).thenReturn(listOf(tile, tile))
+        whenever(qsHost.tiles).thenReturn(listOf(tile, tile))
 
         controller.setTiles()
     }
@@ -111,7 +111,7 @@
     fun testTileSublistWithTooManyTiles() {
         val limit = 3
         whenever(quickQSPanel.numQuickTiles).thenReturn(limit)
-        whenever(qsTileHost.tiles).thenReturn(listOf(tile, tile, tile, tile))
+        whenever(qsHost.tiles).thenReturn(listOf(tile, tile, tile, tile))
 
         controller.setTiles()
 
@@ -147,7 +147,7 @@
 
     class TestQuickQSPanelController(
         view: QuickQSPanel,
-        qsTileHost: QSTileHost,
+        qsHost: QSHost,
         qsCustomizerController: QSCustomizerController,
         usingMediaPlayer: Boolean,
         mediaHost: MediaHost,
@@ -159,7 +159,7 @@
     ) :
         QuickQSPanelController(
             view,
-            qsTileHost,
+            qsHost,
             qsCustomizerController,
             usingMediaPlayer,
             mediaHost,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
index d42cbe3..c041cb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
@@ -25,7 +25,7 @@
 
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -42,19 +42,19 @@
 
     private TileAdapter mTileAdapter;
     @Mock
-    private QSTileHost mQSTileHost;
+    private QSHost mQSHost;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         TestableLooper.get(this).runWithLooper(() -> mTileAdapter =
-                new TileAdapter(mContext, mQSTileHost, new UiEventLoggerFake()));
+                new TileAdapter(mContext, mQSHost, new UiEventLoggerFake()));
     }
 
     @Test
     public void testResetNotifiesHost() {
         mTileAdapter.resetTileSpecs(Collections.emptyList());
-        verify(mQSTileHost).changeTilesByUser(any(), any());
+        verify(mQSHost).changeTilesByUser(any(), any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 040af70..78a0258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -55,7 +55,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -102,7 +102,7 @@
     @Mock
     private TileQueryHelper.TileStateListener mListener;
     @Mock
-    private QSTileHost mQSTileHost;
+    private QSHost mQSHost;
     @Mock
     private PackageManager mPackageManager;
     @Mock
@@ -131,7 +131,7 @@
                         return null;
                     }
                 }
-        ).when(mQSTileHost).createTile(anyString());
+        ).when(mQSHost).createTile(anyString());
         FakeSystemClock clock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(clock);
         mBgExecutor = new FakeExecutor(clock);
@@ -147,7 +147,7 @@
 
     @Test
     public void testIsFinished_trueAfterQuerying() {
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
@@ -156,7 +156,7 @@
 
     @Test
     public void testQueryTiles_callsListenerTwice() {
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
@@ -170,7 +170,7 @@
             return null;
         }).when(mListener).onTilesChanged(any());
 
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
@@ -184,7 +184,7 @@
         mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
                 STOCK_TILES);
 
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
@@ -204,7 +204,7 @@
         mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
                 STOCK_TILES);
 
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
@@ -224,7 +224,7 @@
         mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
                 STOCK_TILES);
 
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
@@ -240,9 +240,9 @@
     public void testCustomTileNotCreated() {
         Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
                 CUSTOM_TILE);
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
-        verify(mQSTileHost, never()).createTile(CUSTOM_TILE);
+        verify(mQSHost, never()).createTile(CUSTOM_TILE);
     }
 
     @Test
@@ -264,7 +264,7 @@
         mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
                 "");
 
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
@@ -278,7 +278,7 @@
         Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
         mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
                 STOCK_TILES);
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
     }
 
     @Test
@@ -286,12 +286,12 @@
         Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
 
         QSTile t = mock(QSTile.class);
-        when(mQSTileHost.createTile("hotspot")).thenReturn(t);
+        when(mQSHost.createTile("hotspot")).thenReturn(t);
 
         mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
                 "hotspot");
 
-        mTileQueryHelper.queryTiles(mQSTileHost);
+        mTileQueryHelper.queryTiles(mQSHost);
 
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
         InOrder verifier = inOrder(t);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 8aa625a..46af89e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
@@ -39,7 +39,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 
 import org.junit.After;
@@ -61,7 +61,7 @@
     @Mock
     private UserTracker mUserTracker;
     @Mock
-    private QSTileHost mQSTileHost;
+    private QSHost mQSHost;
     @Mock
     private Context mMockContext;
 
@@ -80,7 +80,7 @@
         when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM);
 
         when(mTileServices.getContext()).thenReturn(mMockContext);
-        when(mTileServices.getHost()).thenReturn(mQSTileHost);
+        when(mTileServices.getHost()).thenReturn(mQSHost);
         when(mTileLifecycle.getUserId()).thenAnswer(invocation -> mUserTracker.getUserId());
         when(mTileLifecycle.isActiveTile()).thenReturn(false);
 
@@ -98,28 +98,28 @@
 
     @Test
     public void testSetTileAddedIfNotAdded() {
-        when(mQSTileHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSTileHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true);
+        verify(mQSHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true);
     }
 
     @Test
     public void testNotSetTileAddedIfAdded() {
-        when(mQSTileHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true);
+        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSTileHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true));
+        verify(mQSHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true));
     }
 
     @Test
     public void testSetTileAddedCorrectUser() {
         int user = 10;
         when(mUserTracker.getUserId()).thenReturn(user);
-        when(mQSTileHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSTileHost).setTileAdded(mComponentName, user, true);
+        verify(mQSHost).setTileAdded(mComponentName, user, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
index bdfbca4..ccfb5cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
@@ -27,27 +27,27 @@
 import com.android.internal.statusbar.IAddTileResultCallback
 import com.android.systemui.InstanceIdSequenceFake
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -62,7 +62,7 @@
     @Mock
     private lateinit var tileRequestDialog: TileRequestDialog
     @Mock
-    private lateinit var qsTileHost: QSTileHost
+    private lateinit var qsHost: QSHost
     @Mock
     private lateinit var commandRegistry: CommandRegistry
     @Mock
@@ -82,10 +82,10 @@
         `when`(logger.newInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
 
         // Tile not present by default
-        `when`(qsTileHost.indexOf(anyString())).thenReturn(-1)
+        `when`(qsHost.indexOf(anyString())).thenReturn(-1)
 
         controller = TileServiceRequestController(
-                qsTileHost,
+                qsHost,
                 commandQueue,
                 commandRegistry,
                 logger
@@ -107,18 +107,18 @@
 
     @Test
     fun tileAlreadyAdded_correctResult() {
-        `when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
+        `when`(qsHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
 
         val callback = Callback()
         controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
 
         assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.TILE_ALREADY_ADDED)
-        verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
+        verify(qsHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
     }
 
     @Test
     fun tileAlreadyAdded_logged() {
-        `when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
+        `when`(qsHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
 
         controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon) {}
 
@@ -157,7 +157,7 @@
 
         cancelListenerCaptor.value.onCancel(tileRequestDialog)
         assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.DISMISSED)
-        verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
+        verify(qsHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
     }
 
     @Test
@@ -191,7 +191,7 @@
         clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_POSITIVE)
 
         assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.ADD_TILE)
-        verify(qsTileHost).addTile(TEST_COMPONENT, /* end */ true)
+        verify(qsHost).addTile(TEST_COMPONENT, /* end */ true)
     }
 
     @Test
@@ -225,7 +225,7 @@
         clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_NEGATIVE)
 
         assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.DONT_ADD_TILE)
-        verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
+        verify(qsHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
     }
 
     @Test
@@ -266,7 +266,7 @@
 
     @Test
     fun commandQueueCallback_callbackCalled() {
-        `when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
+        `when`(qsHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
         val captor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue, atLeastOnce()).addCallback(capture(captor))
         val c = Callback()
@@ -365,4 +365,4 @@
             accept(r)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 172c87f..64e9a3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -30,7 +30,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
@@ -39,24 +38,13 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSFactoryImpl;
-import com.android.systemui.settings.UserFileManager;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -68,8 +56,6 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
-import java.util.Optional;
-import java.util.concurrent.Executor;
 
 import javax.inject.Provider;
 
@@ -92,26 +78,8 @@
     @Mock
     private StatusBarIconController mStatusBarIconController;
     @Mock
-    private QSFactoryImpl mQSFactory;
-    @Mock
-    private PluginManager mPluginManager;
-    @Mock
-    private  TunerService mTunerService;
-    @Mock
-    private AutoTileManager mAutoTileManager;
-    @Mock
-    private DumpManager mDumpManager;
-    @Mock
-    private CentralSurfaces mCentralSurfaces;
-    @Mock
-    private QSLogger mQSLogger;
-    @Mock
-    private UiEventLogger mUiEventLogger;
-    @Mock
     private UserTracker mUserTracker;
     @Mock
-    private SecureSettings  mSecureSettings;
-    @Mock
     private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
     @Mock
     private TileServiceRequestController mTileServiceRequestController;
@@ -122,12 +90,11 @@
     @Mock
     private TileLifecycleManager mTileLifecycleManager;
     @Mock
-    private UserFileManager mUserFileManager;
+    private QSHost mQSHost;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mDependency.injectMockDependency(BluetoothController.class);
         mManagers = new ArrayList<>();
         mTestableLooper = TestableLooper.get(this);
 
@@ -135,34 +102,16 @@
                 .thenReturn(mTileServiceRequestController);
         when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class)))
                 .thenReturn(mTileLifecycleManager);
+        when(mQSHost.getContext()).thenReturn(mContext);
 
         Provider<Handler> provider = () -> new Handler(mTestableLooper.getLooper());
-        Executor executor = new HandlerExecutor(provider.get());
 
-        QSTileHost host = new QSTileHost(mContext,
-                mStatusBarIconController,
-                mQSFactory,
-                executor,
-                mPluginManager,
-                mTunerService,
-                () -> mAutoTileManager,
-                mDumpManager,
-                Optional.of(mCentralSurfaces),
-                mQSLogger,
-                mUiEventLogger,
-                mUserTracker,
-                mSecureSettings,
-                mock(CustomTileStatePersister.class),
-                mTileServiceRequestControllerBuilder,
-                mTileLifecycleManagerFactory,
-                mUserFileManager);
-        mTileService = new TestTileServices(host, provider, mBroadcastDispatcher,
-                mUserTracker, mKeyguardStateController, mCommandQueue);
+        mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher,
+                mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController);
     }
 
     @After
     public void tearDown() throws Exception {
-        mTileService.getHost().destroy();
         mTileService.destroy();
         TestableLooper.get(this).processAllMessages();
     }
@@ -274,11 +223,12 @@
     }
 
     private class TestTileServices extends TileServices {
-        TestTileServices(QSTileHost host, Provider<Handler> handlerProvider,
+        TestTileServices(QSHost host, Provider<Handler> handlerProvider,
                 BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
-                KeyguardStateController keyguardStateController, CommandQueue commandQueue) {
+                KeyguardStateController keyguardStateController, CommandQueue commandQueue,
+                StatusBarIconController statusBarIconController) {
             super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
-                    commandQueue);
+                    commandQueue, statusBarIconController);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index ba49f3f..36549fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -69,7 +69,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSEvent;
 import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.statusbar.StatusBarState;
 
@@ -97,7 +96,7 @@
     private TestableLooper mTestableLooper;
     private TileImpl mTile;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index d65901777..bf172f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -17,7 +17,7 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.statusbar.policy.BluetoothController
@@ -40,7 +40,7 @@
     @Mock
     private lateinit var qsLogger: QSLogger
     @Mock
-    private lateinit var qsHost: QSTileHost
+    private lateinit var qsHost: QSHost
     @Mock
     private lateinit var metricsLogger: MetricsLogger
     private val falsingManager = FalsingManagerFake()
@@ -135,7 +135,7 @@
     }
 
     private class FakeBluetoothTile(
-        qsTileHost: QSTileHost,
+        qsHost: QSHost,
         backgroundLooper: Looper,
         mainHandler: Handler,
         falsingManager: FalsingManager,
@@ -145,7 +145,7 @@
         qsLogger: QSLogger,
         bluetoothController: BluetoothController
     ) : BluetoothTile(
-        qsTileHost,
+        qsHost,
         backgroundLooper,
         mainHandler,
         falsingManager,
@@ -187,4 +187,4 @@
         `when`(bluetoothController.isBluetoothConnected).thenReturn(false)
         `when`(bluetoothController.isBluetoothConnecting).thenReturn(true)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index b40a20c..18f891c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -42,7 +42,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -78,7 +78,7 @@
     @Mock
     private NetworkController mNetworkController;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     SignalCallback mSignalCallback;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
index debe41c..fdb63ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
@@ -37,7 +37,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.settings.FakeSettings;
@@ -56,7 +56,7 @@
 public class ColorCorrectionTileTest extends SysuiTestCase {
 
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index 3fd2501..60c1a33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -39,7 +39,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.settings.UserTracker;
@@ -61,7 +61,7 @@
     private static final Integer COLOR_INVERSION_ENABLED = 1;
 
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index 15545a4..71d8aa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -47,7 +47,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.settings.UserTracker;
@@ -69,7 +69,7 @@
     @Mock
     private ActivityStarter mActivityStarter;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index d0f851b..c7aba1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -13,7 +13,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.statusbar.policy.FlashlightController
@@ -34,7 +34,7 @@
 
     @Mock private lateinit var qsLogger: QSLogger
 
-    @Mock private lateinit var qsHost: QSTileHost
+    @Mock private lateinit var qsHost: QSHost
 
     @Mock private lateinit var metricsLogger: MetricsLogger
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index 451e911..4a2ac96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -37,7 +37,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.DataSaverController;
@@ -60,7 +60,7 @@
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private HotspotController mHotspotController;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index addca9d..abd9094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-
 import android.os.Handler;
 import android.service.quicksettings.Tile;
 import android.testing.AndroidTestingRunner;
@@ -35,7 +34,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
@@ -56,7 +55,7 @@
 public class InternetTileTest extends SysuiTestCase {
 
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private NetworkController mNetworkController;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index d2bbc8c..08d10fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -29,7 +29,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -52,7 +52,7 @@
     @Mock
     private lateinit var qsLogger: QSLogger
     @Mock
-    private lateinit var qsHost: QSTileHost
+    private lateinit var qsHost: QSHost
     @Mock
     private lateinit var metricsLogger: MetricsLogger
     private val falsingManager = FalsingManagerFake()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index cfd3735..9638a45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -36,7 +36,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 
 import org.junit.Before;
@@ -60,7 +60,7 @@
     @Mock
     private ActivityStarter mActivityStarter;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
index 8031875..3344a17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
@@ -32,7 +32,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.settings.SecureSettings;
@@ -53,7 +53,7 @@
     @Mock
     private ActivityStarter mActivityStarter;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index a1be2f3..24287ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -39,7 +39,7 @@
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
@@ -54,7 +54,7 @@
 @SmallTest
 public class QRCodeScannerTileTest extends SysuiTestCase {
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 4f6475f..4722c8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -66,7 +66,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -99,7 +99,7 @@
             .setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
 
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 04b372c..99e5564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -38,7 +38,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -57,7 +57,7 @@
 @Ignore("b/269171747")
 public class ReduceBrightColorsTileTest extends SysuiTestCase {
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index e9dfd3e..c7eb2f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -38,7 +38,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -71,7 +71,7 @@
     @Mock
     private ActivityStarter mActivityStarter;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 30debdf..21acc08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -43,7 +43,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.screenrecord.RecordingController;
@@ -65,7 +65,7 @@
     @Mock
     private RecordingController mController;
     @Mock
-    private QSTileHost mHost;
+    private QSHost mHost;
     @Mock
     private KeyguardDismissUtil mKeyguardDismissUtil;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index 0c070da..3d9f650 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.statusbar.policy.BatteryController
@@ -55,7 +55,7 @@
     @Mock private lateinit var uiModeManager: UiModeManager
     @Mock private lateinit var resources: Resources
     @Mock private lateinit var qsLogger: QSLogger
-    @Mock private lateinit var qsHost: QSTileHost
+    @Mock private lateinit var qsHost: QSHost
     @Mock private lateinit var metricsLogger: MetricsLogger
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var activityStarter: ActivityStarter
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 452606d..8ee1ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -44,6 +44,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -354,7 +355,8 @@
                     mDeviceProvisionedController,
                     mKeyguardStateController,
                     mSettings,
-                    mock(DumpManager.class));
+                    mock(DumpManager.class),
+                    mock(LockPatternUtils.class));
         }
 
         public BroadcastReceiver getBaseBroadcastReceiverForTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt
new file mode 100644
index 0000000..2de21ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.view.View
+
+class FakeNodeController(
+    override val view: View,
+    override val nodeLabel: String = "fakeNodeController"
+) : NodeController {
+    override fun offerToKeepInParentForAnimation(): Boolean = false
+    override fun removeFromParentIfKeptForAnimation(): Boolean = false
+    override fun resetKeepInParentForAnimation() = Unit
+}
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
index 5b6c8c6..fb3aba1 100644
--- 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
@@ -31,15 +31,19 @@
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.SmartReplyController
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
+import com.android.systemui.statusbar.notification.collection.render.FakeNodeController
 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.NotificationChildrenContainer
 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.any
+import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.wmshell.BubblesManager
@@ -68,6 +72,7 @@
     private val metricsLogger: MetricsLogger = mock()
     private val logBufferLogger: NotificationRowLogger = mock()
     private val listContainer: NotificationListContainer = mock()
+    private val childrenContainer: NotificationChildrenContainer = mock()
     private val mediaManager: NotificationMediaManager = mock()
     private val smartReplyConstants: SmartReplyConstants = mock()
     private val smartReplyController: SmartReplyController = mock()
@@ -129,6 +134,7 @@
                 dragController,
                 dismissibilityProvider
             )
+        whenever(view.childrenContainer).thenReturn(childrenContainer)
     }
 
     @After
@@ -173,4 +179,32 @@
         Assert.assertFalse(controller.removeFromParentIfKeptForAnimation())
         Mockito.verifyNoMoreInteractions(parentView)
     }
+
+    @Test
+    fun removeChild_whenTransfer() {
+        val childView: ExpandableNotificationRow = mock()
+        val childNodeController = FakeNodeController(childView)
+
+        // GIVEN a child is removed for transfer
+        controller.removeChild(childNodeController, /* isTransfer= */ true)
+
+        // VERIFY the listContainer is not notified
+        Mockito.verify(childView).isChangingPosition = eq(true)
+        Mockito.verify(view).removeChildNotification(eq(childView))
+        Mockito.verify(listContainer, never()).notifyGroupChildRemoved(any(), any())
+    }
+
+    @Test
+    fun removeChild_whenNotTransfer() {
+        val childView: ExpandableNotificationRow = mock()
+        val childNodeController = FakeNodeController(childView)
+
+        // GIVEN a child is removed for real
+        controller.removeChild(childNodeController, /* isTransfer= */ false)
+
+        // VERIFY the listContainer is passed the childrenContainer for transient animations
+        Mockito.verify(childView, never()).isChangingPosition = any()
+        Mockito.verify(view).removeChildNotification(eq(childView))
+        Mockito.verify(listContainer).notifyGroupChildRemoved(eq(childView), eq(childrenContainer))
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index f568547..e680a4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -53,7 +53,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.qs.SettingObserver;
 import com.android.systemui.qs.external.CustomTile;
@@ -104,7 +104,7 @@
 
     private static final int USER = 0;
 
-    @Mock private QSTileHost mQsTileHost;
+    @Mock private QSHost mQsHost;
     @Mock private AutoAddTracker mAutoAddTracker;
     @Mock private CastController mCastController;
     @Mock private HotspotController mHotspotController;
@@ -144,7 +144,7 @@
                 R.string.safety_quick_settings_tile_class, TEST_CUSTOM_SAFETY_CLASS);
 
         when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
-        when(mQsTileHost.getUserContext()).thenReturn(mUserContext);
+        when(mQsHost.getUserContext()).thenReturn(mUserContext);
         when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
         mPackageManager = Mockito.spy(mContext.getPackageManager());
         when(mPackageManager.getPermissionControllerPackageName())
@@ -174,7 +174,7 @@
             WalletController walletController,
             SafetyController safetyController,
             @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
-        return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost,
+        return new AutoTileManager(context, autoAddTrackerBuilder, mQsHost,
                 Handler.createAsync(TestableLooper.get(this).getLooper()),
                 mSecureSettings,
                 hotspotController,
@@ -359,7 +359,7 @@
             return;
         }
         mAutoTileManager.mNightDisplayCallback.onActivated(true);
-        verify(mQsTileHost).addTile("night");
+        verify(mQsHost).addTile("night");
     }
 
     @Test
@@ -368,7 +368,7 @@
             return;
         }
         mAutoTileManager.mNightDisplayCallback.onActivated(false);
-        verify(mQsTileHost, never()).addTile("night");
+        verify(mQsHost, never()).addTile("night");
     }
 
     @Test
@@ -378,7 +378,7 @@
         }
         mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
                 ColorDisplayManager.AUTO_MODE_TWILIGHT);
-        verify(mQsTileHost).addTile("night");
+        verify(mQsHost).addTile("night");
     }
 
     @Test
@@ -388,7 +388,7 @@
         }
         mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
                 ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
-        verify(mQsTileHost).addTile("night");
+        verify(mQsHost).addTile("night");
     }
 
     @Test
@@ -398,19 +398,19 @@
         }
         mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
                 ColorDisplayManager.AUTO_MODE_DISABLED);
-        verify(mQsTileHost, never()).addTile("night");
+        verify(mQsHost, never()).addTile("night");
     }
 
     @Test
     public void reduceBrightColorsTileAdded_whenActivated() {
         mAutoTileManager.mReduceBrightColorsCallback.onActivated(true);
-        verify(mQsTileHost).addTile("reduce_brightness");
+        verify(mQsHost).addTile("reduce_brightness");
     }
 
     @Test
     public void reduceBrightColorsTileNotAdded_whenDeactivated() {
         mAutoTileManager.mReduceBrightColorsCallback.onActivated(false);
-        verify(mQsTileHost, never()).addTile("reduce_brightness");
+        verify(mQsHost, never()).addTile("reduce_brightness");
     }
 
     private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
@@ -423,28 +423,28 @@
     public void castTileAdded_whenDeviceIsCasting() {
         doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices();
         mAutoTileManager.mCastCallback.onCastDevicesChanged();
-        verify(mQsTileHost).addTile("cast");
+        verify(mQsHost).addTile("cast");
     }
 
     @Test
     public void castTileNotAdded_whenDeviceIsNotCasting() {
         doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices();
         mAutoTileManager.mCastCallback.onCastDevicesChanged();
-        verify(mQsTileHost, never()).addTile("cast");
+        verify(mQsHost, never()).addTile("cast");
     }
 
     @Test
     public void testSettingTileAdded_onChanged() {
         changeValue(TEST_SETTING, 1);
         verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
-        verify(mQsTileHost).addTile(TEST_SPEC);
+        verify(mQsHost).addTile(TEST_SPEC);
     }
 
     @Test
     public void testSettingTileAddedComponentAtEnd_onChanged() {
         changeValue(TEST_SETTING_COMPONENT, 1);
         verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC);
-        verify(mQsTileHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT)
+        verify(mQsHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT)
             , /* end */ true);
     }
 
@@ -453,14 +453,14 @@
         changeValue(TEST_SETTING, 1);
         changeValue(TEST_SETTING, 2);
         verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
-        verify(mQsTileHost).addTile(TEST_SPEC);
+        verify(mQsHost).addTile(TEST_SPEC);
     }
 
     @Test
     public void testSettingTileNotAdded_onChangedTo0() {
         changeValue(TEST_SETTING, 0);
         verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
-        verify(mQsTileHost, never()).addTile(TEST_SPEC);
+        verify(mQsHost, never()).addTile(TEST_SPEC);
     }
 
     @Test
@@ -469,27 +469,27 @@
 
         changeValue(TEST_SETTING, 1);
         verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
-        verify(mQsTileHost, never()).addTile(TEST_SPEC);
+        verify(mQsHost, never()).addTile(TEST_SPEC);
     }
 
     @Test
     public void testSafetyTileNotAdded_ifPreviouslyAdded() {
         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
         mAutoTileManager.init();
-        verify(mQsTileHost, times(1)).addTile(safetyComponent, true);
+        verify(mQsHost, times(1)).addTile(safetyComponent, true);
         when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
         mAutoTileManager.init();
-        verify(mQsTileHost, times(1)).addTile(safetyComponent, true);
+        verify(mQsHost, times(1)).addTile(safetyComponent, true);
     }
 
     @Test
     public void testSafetyTileAdded_onUserChange() {
         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
         mAutoTileManager.init();
-        verify(mQsTileHost, times(1)).addTile(safetyComponent, true);
+        verify(mQsHost, times(1)).addTile(safetyComponent, true);
         when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(false);
         mAutoTileManager.changeUser(UserHandle.of(USER + 1));
-        verify(mQsTileHost, times(2)).addTile(safetyComponent, true);
+        verify(mQsHost, times(2)).addTile(safetyComponent, true);
     }
 
     @Test
@@ -498,17 +498,17 @@
         mAutoTileManager.init();
         when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
         mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
-        verify(mQsTileHost, times(1)).removeTile(TEST_CUSTOM_SAFETY_SPEC);
+        verify(mQsHost, times(1)).removeTile(TEST_CUSTOM_SAFETY_SPEC);
     }
 
     @Test
     public void testSafetyTileAdded_onSafetyCenterEnable() {
         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
         mAutoTileManager.init();
-        verify(mQsTileHost, times(1)).addTile(safetyComponent, true);
+        verify(mQsHost, times(1)).addTile(safetyComponent, true);
         mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
         mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(true);
-        verify(mQsTileHost, times(2)).addTile(safetyComponent, true);
+        verify(mQsHost, times(2)).addTile(safetyComponent, true);
     }
 
     @Test
@@ -525,7 +525,7 @@
 
         mManagedProfileCallback.onManagedProfileChanged();
 
-        verify(mQsTileHost, times(1)).addTile(eq("work"), eq(2));
+        verify(mQsHost, times(1)).addTile(eq("work"), eq(2));
         verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
     }
 
@@ -542,7 +542,7 @@
 
         mManagedProfileCallback.onManagedProfileChanged();
 
-        verify(mQsTileHost, times(1)).removeTile(eq("work"));
+        verify(mQsHost, times(1)).removeTile(eq("work"));
         verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
     }
 
@@ -550,7 +550,7 @@
     public void testAddControlsTileIfNotPresent() {
         String spec = DEVICE_CONTROLS;
         when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
-        when(mQsTileHost.getTiles()).thenReturn(new ArrayList<>());
+        when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
 
         mAutoTileManager.init();
         ArgumentCaptor<DeviceControlsController.Callback> captor =
@@ -559,7 +559,7 @@
         verify(mDeviceControlsController).setCallback(captor.capture());
 
         captor.getValue().onControlsUpdate(3);
-        verify(mQsTileHost).addTile(spec, 3);
+        verify(mQsHost).addTile(spec, 3);
         verify(mAutoAddTracker).setTileAdded(spec);
     }
 
@@ -567,7 +567,7 @@
     public void testDontAddControlsTileIfPresent() {
         String spec = DEVICE_CONTROLS;
         when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
-        when(mQsTileHost.getTiles()).thenReturn(new ArrayList<>());
+        when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
 
         mAutoTileManager.init();
         ArgumentCaptor<DeviceControlsController.Callback> captor =
@@ -576,7 +576,7 @@
         verify(mDeviceControlsController).setCallback(captor.capture());
 
         captor.getValue().removeControlsAutoTracker();
-        verify(mQsTileHost, never()).addTile(spec, 3);
+        verify(mQsHost, never()).addTile(spec, 3);
         verify(mAutoAddTracker, never()).setTileAdded(spec);
         verify(mAutoAddTracker).setTileRemoved(spec);
     }
@@ -587,7 +587,7 @@
         when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(true);
         QSTile mockTile = mock(QSTile.class);
         when(mockTile.getTileSpec()).thenReturn(spec);
-        when(mQsTileHost.getTiles()).thenReturn(List.of(mockTile));
+        when(mQsHost.getTiles()).thenReturn(List.of(mockTile));
 
         mAutoTileManager.init();
         ArgumentCaptor<DeviceControlsController.Callback> captor =
@@ -596,7 +596,7 @@
         verify(mDeviceControlsController).setCallback(captor.capture());
 
         captor.getValue().onControlsUpdate(3);
-        verify(mQsTileHost, never()).addTile(spec, 3);
+        verify(mQsHost, never()).addTile(spec, 3);
         verify(mAutoAddTracker, never()).setTileAdded(spec);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 1e2fe35..31a1e4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
 import com.android.systemui.shade.NotificationPanelViewController;
@@ -94,6 +95,7 @@
     @Mock private SystemBarAttributesListener mSystemBarAttributesListener;
     @Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
     @Mock private UserTracker mUserTracker;
+    @Mock private QSHost mQSHost;
 
     CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
 
@@ -128,7 +130,8 @@
                 DEFAULT_DISPLAY,
                 mSystemBarAttributesListener,
                 mCameraLauncherLazy,
-                mUserTracker);
+                mUserTracker,
+                mQSHost);
 
         when(mUserTracker.getUserHandle()).thenReturn(
                 UserHandle.of(ActivityManager.getCurrentUser()));
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 0926f8a..a359216 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -142,7 +142,7 @@
     protected final SystemSupport mSystemSupport;
     protected final WindowManagerInternal mWindowManagerService;
     private final SystemActionPerformer mSystemActionPerformer;
-    private final AccessibilityWindowManager mA11yWindowManager;
+    final AccessibilityWindowManager mA11yWindowManager;
     private final DisplayManager mDisplayManager;
     private final PowerManager mPowerManager;
     private final IPlatformCompat mIPlatformCompat;
@@ -2006,15 +2006,14 @@
         return accessibilityWindowId;
     }
 
-    private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
+    int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
         if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) {
             final int focusedWindowId = mA11yWindowManager.getFocusedWindowId(focusType);
             // If the caller is a proxy and the found window doesn't belong to a proxy display
             // (or vice versa), then return null. This doesn't work if there are multiple active
-            // proxys, but in the future this code shouldn't be needed if input and a11y focus are
+            // proxys, but in the future this code shouldn't be needed if input focus are
             // properly split. (so we will deal with the issues if we see them).
-            //TODO(254545943): Remove this when there is user and proxy separation of input and a11y
-            // focus
+            //TODO(254545943): Remove this when there is user and proxy separation of input focus
             if (!mA11yWindowManager.windowIdBelongsToDisplayType(focusedWindowId, mDisplayTypes)) {
                 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index cde820a..1f8a779 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3823,7 +3823,7 @@
         try {
             mProxyManager.registerProxy(client, displayId, mContext,
                     sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(),
-                    mWindowManagerService, mA11yWindowManager);
+                    mWindowManagerService);
 
             synchronized (mLock) {
                 notifyClearAccessibilityCacheLocked();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index f0c6c4f..094053e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -19,6 +19,7 @@
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
@@ -104,6 +105,8 @@
 
     private boolean mTouchInteractionInProgress;
 
+    private boolean mHasProxy;
+
     /** List of Display Windows Observer, mapping from displayId -> DisplayWindowsObserver. */
     private final SparseArray<DisplayWindowsObserver> mDisplayWindowsObservers =
             new SparseArray<>();
@@ -159,6 +162,9 @@
      * Returns {@code true} if the window belongs to a display of {@code displayTypes}.
      */
     public boolean windowIdBelongsToDisplayType(int focusedWindowId, int displayTypes) {
+        if (!mHasProxy) {
+            return true;
+        }
         // UIAutomation wants focus from any display type.
         final int displayTypeMask = DISPLAY_TYPE_PROXY | DISPLAY_TYPE_DEFAULT;
         if ((displayTypes & displayTypeMask) == displayTypeMask) {
@@ -195,6 +201,7 @@
         private List<AccessibilityWindowInfo> mWindows;
         private boolean mTrackingWindows = false;
         private boolean mHasWatchOutsideTouchWindow;
+        private int mProxyDisplayAccessibilityFocusedWindow;
         private boolean mIsProxy;
 
         /**
@@ -608,8 +615,11 @@
 
             final int windowCount = windows.size();
             final boolean isTopFocusedDisplay = mDisplayId == mTopFocusedDisplayId;
+            // A proxy with an a11y-focused window is a11y-focused should use the proxy focus id.
             final boolean isAccessibilityFocusedDisplay =
-                    mDisplayId == mAccessibilityFocusedDisplayId;
+                    mDisplayId == mAccessibilityFocusedDisplayId
+                            || (mIsProxy && mProxyDisplayAccessibilityFocusedWindow
+                                    != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
             // Modifies the value of top focused window, active window and a11y focused window
             // only if this display is top focused display which has the top focused window.
             if (isTopFocusedDisplay) {
@@ -635,9 +645,12 @@
 
             // We'll clear accessibility focus if the window with focus is no longer visible to
             // accessibility services.
+            int a11yFocusedWindowId = mIsProxy
+                    ? mProxyDisplayAccessibilityFocusedWindow
+                    : mAccessibilityFocusedWindowId;
             if (isAccessibilityFocusedDisplay) {
-                shouldClearAccessibilityFocus = mAccessibilityFocusedWindowId
-                    != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                shouldClearAccessibilityFocus = a11yFocusedWindowId
+                        != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
             }
 
             boolean hasWindowIgnore = false;
@@ -701,7 +714,7 @@
                 if (isAccessibilityFocusedDisplay) {
                     for (int i = 0; i < accessibilityWindowCount; i++) {
                         final AccessibilityWindowInfo window = mWindows.get(i);
-                        if (window.getId() == mAccessibilityFocusedWindowId) {
+                        if (window.getId() == a11yFocusedWindowId) {
                             window.setAccessibilityFocused(true);
                             shouldClearAccessibilityFocus = false;
                             break;
@@ -718,7 +731,7 @@
             }
 
             if (shouldClearAccessibilityFocus) {
-                clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId);
+                clearAccessibilityFocusLocked(a11yFocusedWindowId);
             }
         }
 
@@ -1022,6 +1035,7 @@
             }
             if (proxyed && !observer.mIsProxy) {
                 observer.mIsProxy = true;
+                mHasProxy = true;
             }
             if (observer.isTrackingWindowsLocked()) {
                 return;
@@ -1044,6 +1058,7 @@
                 observer.stopTrackingWindowsLocked();
                 mDisplayWindowsObservers.remove(displayId);
             }
+            resetHasProxyIfNeededLocked();
         }
     }
 
@@ -1053,11 +1068,26 @@
      */
     public void stopTrackingDisplayProxy(int displayId) {
         synchronized (mLock) {
-            DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+            final DisplayWindowsObserver proxyObserver = mDisplayWindowsObservers.get(displayId);
+            if (proxyObserver != null) {
+                proxyObserver.mIsProxy = false;
+            }
+            resetHasProxyIfNeededLocked();
+        }
+    }
+
+    private void resetHasProxyIfNeededLocked() {
+        boolean hasProxy = false;
+        final int count = mDisplayWindowsObservers.size();
+        for (int i = 0; i < count; i++) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
             if (observer != null) {
-                observer.mIsProxy = false;
+                if (observer.mIsProxy) {
+                    hasProxy = true;
+                }
             }
         }
+        mHasProxy = hasProxy;
     }
 
     /**
@@ -1490,6 +1520,11 @@
 
             case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
                 synchronized (mLock) {
+                    // If window id belongs to a proxy display, then find the display, update the
+                    // observer focus and send WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED events.
+                    if (mHasProxy && setProxyFocusLocked(windowId)) {
+                        return;
+                    }
                     if (mAccessibilityFocusedWindowId != windowId) {
                         clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId);
                         setAccessibilityFocusedWindowLocked(windowId);
@@ -1500,6 +1535,10 @@
 
             case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
                 synchronized (mLock) {
+                    // If cleared happened on the proxy display, then clear the tracked focus.
+                    if (mHasProxy && clearProxyFocusLocked(windowId, eventAction)) {
+                        return;
+                    }
                     if (mAccessibilityFocusNodeId == nodeId) {
                         mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                     }
@@ -1599,9 +1638,10 @@
             if (mAccessibilityFocusedDisplayId != Display.INVALID_DISPLAY
                     && mAccessibilityFocusedWindowId
                     != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+                // Previously focused window -> send a focused event for losing focus
                 events.add(AccessibilityEvent.obtainWindowsChangedEvent(
                         mAccessibilityFocusedDisplayId, mAccessibilityFocusedWindowId,
-                        AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+                        WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
             }
 
             mAccessibilityFocusedWindowId = windowId;
@@ -1611,8 +1651,9 @@
                 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
                 if (observer != null && observer.setAccessibilityFocusedWindowLocked(windowId)) {
                     mAccessibilityFocusedDisplayId = observer.mDisplayId;
+                    // Newly focused window -> send a focused event for gaining focus
                     events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
-                            windowId, AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+                            windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
                 }
             }
 
@@ -1662,6 +1703,33 @@
      * @return The focused windowId
      */
     public int getFocusedWindowId(int focusType) {
+        return getFocusedWindowId(focusType, Display.INVALID_DISPLAY);
+    }
+
+    /**
+     * Returns focused windowId or accessibility focused windowId according to given focusType and
+     * display id.
+     * @param focusType {@link AccessibilityNodeInfo#FOCUS_INPUT} or
+     * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}
+     * @param displayId the display id to check. If this display is proxy-ed, the proxy's a11y focus
+     *                  will be returned.
+     * @return The focused windowId
+     */
+    public int getFocusedWindowId(int focusType, int displayId) {
+        if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY
+                || !mHasProxy) {
+            return getDefaultFocus(focusType);
+        }
+
+        final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+        if (observer != null && observer.mIsProxy) {
+            return getProxyFocus(focusType, observer);
+        } else {
+            return getDefaultFocus(focusType);
+        }
+    }
+
+    private int getDefaultFocus(int focusType) {
         if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
             return mTopFocusedWindowId;
         } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
@@ -1670,6 +1738,16 @@
         return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
     }
 
+    private int getProxyFocus(int focusType, DisplayWindowsObserver observer) {
+        if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
+            return mTopFocusedWindowId;
+        } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
+            return observer.mProxyDisplayAccessibilityFocusedWindow;
+        } else {
+            return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+        }
+    }
+
     /**
      * Returns {@link AccessibilityWindowInfo} of PIP window.
      *
@@ -1988,6 +2066,78 @@
     }
 
     /**
+     * Checks if the window belongs to a proxy display and if so clears the focused window id.
+     * @param focusClearedWindowId the cleared window id.
+     * @return true if an observer is proxy-ed and has cleared its focused window id.
+     */
+    private boolean clearProxyFocusLocked(int focusClearedWindowId, int eventAction) {
+        // If we are just moving focus from one view to the other in the same window, do nothing.
+        if (eventAction == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
+            return false;
+        }
+        for (int i = 0; i < mDisplayWindowsObservers.size(); i++) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(i);
+            if (observer != null && observer.mWindows != null && observer.mIsProxy) {
+                final int windowCount = observer.mWindows.size();
+                for (int j = 0; j < windowCount; j++) {
+                    AccessibilityWindowInfo window = observer.mWindows.get(j);
+                    if (window.getId() == focusClearedWindowId) {
+                        observer.mProxyDisplayAccessibilityFocusedWindow =
+                                AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                        // TODO(268754409): Look into sending a WINDOW_FOCUS_CHANGED event since
+                        //  window no longer has focus (default window logic doesn't), and
+                        //  whether the node id needs to be cached (default window logic does).
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the window belongs to a proxy display and if so sends
+     * WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED for that window and the previously focused window.
+     * @param focusedWindowId the focused window id.
+     * @return true if an observer is proxy-ed and contains the focused window.
+     */
+    private boolean setProxyFocusLocked(int focusedWindowId) {
+        for (int i = 0; i < mDisplayWindowsObservers.size(); i++) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+            if (observer != null && observer.mIsProxy
+                    && observer.setAccessibilityFocusedWindowLocked(focusedWindowId)) {
+                final int previouslyFocusedWindowId =
+                        observer.mProxyDisplayAccessibilityFocusedWindow;
+
+                if (previouslyFocusedWindowId == focusedWindowId) {
+                    // Don't send a focus event if the window is already focused.
+                    return true;
+                }
+
+                // Previously focused window -> Clear focus on UI thread and send a focused event
+                // for losing focus
+                if (previouslyFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+                    clearAccessibilityFocusLocked(previouslyFocusedWindowId);
+                    mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
+                            AccessibilityEvent.obtainWindowsChangedEvent(
+                                    observer.mDisplayId, previouslyFocusedWindowId,
+                                    WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+                }
+                observer.mProxyDisplayAccessibilityFocusedWindow = focusedWindowId;
+                // Newly focused window -> send a focused event for it gaining focus
+                mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
+                        AccessibilityEvent.obtainWindowsChangedEvent(
+                                observer.mDisplayId,
+                                observer.mProxyDisplayAccessibilityFocusedWindow,
+                                WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Dumps all {@link AccessibilityWindowInfo}s here.
      */
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index df913aa..945d43b 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -255,6 +255,24 @@
     }
 
     @Override
+    int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) {
+        if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) {
+            final int focusedWindowId = mA11yWindowManager.getFocusedWindowId(focusType,
+                    mDisplayId);
+            // If the caller is a proxy and the found window doesn't belong to a proxy display
+            // (or vice versa), then return null. This doesn't work if there are multiple active
+            // proxys, but in the future this code shouldn't be needed if input focus
+            // properly split. (so we will deal with the issues if we see them).
+            //TODO(254545943): Remove this when there is user and proxy separation of input
+            if (!mA11yWindowManager.windowIdBelongsToDisplayType(focusedWindowId, mDisplayTypes)) {
+                return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+            }
+            return focusedWindowId;
+        }
+        return windowId;
+    }
+
+    @Override
     public void binderDied() {
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index 2530338..9d91d10 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -78,8 +78,7 @@
             AccessibilitySecurityPolicy securityPolicy,
             AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
             AccessibilityTrace trace,
-            WindowManagerInternal windowManagerInternal,
-            AccessibilityWindowManager awm) throws RemoteException {
+            WindowManagerInternal windowManagerInternal) throws RemoteException {
 
         // Set a default AccessibilityServiceInfo that is used before the proxy's info is
         // populated. A proxy has the touch exploration and window capabilities.
@@ -93,7 +92,7 @@
                 new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
                         id, mainHandler, mLock, securityPolicy, systemSupport, trace,
                         windowManagerInternal,
-                        awm, displayId);
+                        mA11yWindowManager, displayId);
 
         synchronized (mLock) {
             mProxyA11yServiceConnections.put(displayId, connection);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 9c84c04..f85ef43f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -44,6 +44,8 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MathUtils;
@@ -279,7 +281,8 @@
         return mTempPointerProperties;
     }
 
-    private void transitionTo(State state) {
+    @VisibleForTesting
+    void transitionTo(State state) {
         if (DEBUG_STATE_TRANSITIONS) {
             Slog.i(mLogTag,
                     (State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
@@ -287,6 +290,9 @@
                     .replace(getClass().getName(), ""));
         }
         mPreviousState = mCurrentState;
+        if (state == mPanningScalingState) {
+            mPanningScalingState.prepareForState();
+        }
         mCurrentState = state;
     }
 
@@ -317,18 +323,34 @@
     final class PanningScalingState extends SimpleOnGestureListener
             implements OnScaleGestureListener, State {
 
+        private final Context mContext;
         private final ScaleGestureDetector mScaleGestureDetector;
         private final GestureDetector mScrollGestureDetector;
         final float mScalingThreshold;
 
         float mInitialScaleFactor = -1;
-        boolean mScaling;
+        @VisibleForTesting boolean mScaling;
+
+        /**
+         * Whether it needs to detect the target scale passes
+         * {@link FullScreenMagnificationController#getPersistedScale} during panning scale.
+         */
+        @VisibleForTesting boolean mDetectingPassPersistedScale;
+
+        // The threshold for relative difference from given scale to persisted scale. If the
+        // difference >= threshold, we can start detecting if the scale passes the persisted
+        // scale during panning.
+        @VisibleForTesting static final float CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD = 0.2f;
+        // The threshold for relative difference from given scale to persisted scale. If the
+        // difference < threshold, we can decide that the scale passes the persisted scale.
+        @VisibleForTesting static final float PASSING_PERSISTED_SCALE_THRESHOLD = 0.01f;
 
         PanningScalingState(Context context) {
             final TypedValue scaleValue = new TypedValue();
             context.getResources().getValue(
                     R.dimen.config_screen_magnification_scaling_threshold,
                     scaleValue, false);
+            mContext = context;
             mScalingThreshold = scaleValue.getFloat();
             mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
             mScaleGestureDetector.setQuickScaleEnabled(false);
@@ -351,12 +373,59 @@
             }
         }
 
+
+        void prepareForState() {
+            checkShouldDetectPassPersistedScale();
+        }
+
+        private void checkShouldDetectPassPersistedScale() {
+            if (mDetectingPassPersistedScale) {
+                return;
+            }
+
+            final float currentScale =
+                    mFullScreenMagnificationController.getScale(mDisplayId);
+            final float persistedScale =
+                    mFullScreenMagnificationController.getPersistedScale(mDisplayId);
+
+            mDetectingPassPersistedScale =
+                    (abs(currentScale - persistedScale) / persistedScale)
+                            >= CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+        }
+
         public void persistScaleAndTransitionTo(State state) {
             mFullScreenMagnificationController.persistScale(mDisplayId);
             clear();
             transitionTo(state);
         }
 
+        @VisibleForTesting
+        void setScaleAndClearIfNeeded(float scale, float pivotX, float pivotY) {
+            if (mDetectingPassPersistedScale) {
+                final float persistedScale =
+                        mFullScreenMagnificationController.getPersistedScale(mDisplayId);
+                // If the scale passes the persisted scale during panning, perform a vibration
+                // feedback to user. Also, call {@link clear} to create a buffer zone so that
+                // user needs to panning more than {@link mScalingThreshold} to change scale again.
+                if (abs(scale - persistedScale) / persistedScale
+                        < PASSING_PERSISTED_SCALE_THRESHOLD) {
+                    scale = persistedScale;
+                    final Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+                    if (vibrator != null) {
+                        vibrator.vibrate(
+                                VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
+                    }
+                    clear();
+                }
+            }
+
+            if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
+            mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+            checkShouldDetectPassPersistedScale();
+        }
+
         @Override
         public boolean onScroll(MotionEvent first, MotionEvent second,
                 float distanceX, float distanceY) {
@@ -402,11 +471,7 @@
                 scale = targetScale;
             }
 
-            final float pivotX = detector.getFocusX();
-            final float pivotY = detector.getFocusY();
-            if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
-            mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
-                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+            setScaleAndClearIfNeeded(scale, detector.getFocusX(), detector.getFocusY());
             return /* handled: */ true;
         }
 
@@ -424,6 +489,7 @@
         public void clear() {
             mInitialScaleFactor = -1;
             mScaling = false;
+            mDetectingPassPersistedScale = false;
         }
 
         @Override
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index b4dcf43..f650560 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -51,6 +51,10 @@
 import android.content.pm.ActivityInfo;
 import android.graphics.PointF;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
 import android.hardware.input.VirtualDpadConfig;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualKeyboardConfig;
@@ -82,6 +86,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.server.LocalServices;
 import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
 import com.android.server.companion.virtual.audio.VirtualAudioController;
 
@@ -109,21 +114,29 @@
 
     private final Context mContext;
     private final AssociationInfo mAssociationInfo;
+    private final VirtualDeviceManagerService mService;
     private final PendingTrampolineCallback mPendingTrampolineCallback;
     private final int mOwnerUid;
     private final int mDeviceId;
+    // Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
+    // Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
+    // 1. After display is created the window manager calls into VDM during construction
+    //   of display specific context to fetch device id corresponding to the display.
+    //   mVirtualDeviceLock will be held while this is done.
+    // 2. InputController interactions result in calls to DisplayManager (to set IME,
+    //    possibly more indirect calls), and those attempt to lock GlobalWindowManagerLock which
+    //    creates lock inversion.
     private final InputController mInputController;
     private final SensorController mSensorController;
     private final CameraAccessController mCameraAccessController;
     private VirtualAudioController mVirtualAudioController;
-    @VisibleForTesting
-    final ArraySet<Integer> mVirtualDisplayIds = new ArraySet<>();
-    private final OnDeviceCloseListener mOnDeviceCloseListener;
     private final IBinder mAppToken;
     private final VirtualDeviceParams mParams;
-    private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
+    @GuardedBy("mVirtualDeviceLock")
+    private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
     private final IVirtualDeviceActivityListener mActivityListener;
     private final IVirtualDeviceSoundEffectListener mSoundEffectListener;
+    private final DisplayManagerGlobal mDisplayManager;
     @GuardedBy("mVirtualDeviceLock")
     private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
     @NonNull
@@ -174,21 +187,14 @@
         };
     }
 
-    /**
-     * A mapping from the virtual display ID to its corresponding
-     * {@link GenericWindowPolicyController}.
-     */
-    private final SparseArray<GenericWindowPolicyController> mWindowPolicyControllers =
-            new SparseArray<>();
-
     VirtualDeviceImpl(
             Context context,
             AssociationInfo associationInfo,
+            VirtualDeviceManagerService service,
             IBinder token,
             int ownerUid,
             int deviceId,
             CameraAccessController cameraAccessController,
-            OnDeviceCloseListener onDeviceCloseListener,
             PendingTrampolineCallback pendingTrampolineCallback,
             IVirtualDeviceActivityListener activityListener,
             IVirtualDeviceSoundEffectListener soundEffectListener,
@@ -197,40 +203,43 @@
         this(
                 context,
                 associationInfo,
+                service,
                 token,
                 ownerUid,
                 deviceId,
                 /* inputController= */ null,
                 /* sensorController= */ null,
                 cameraAccessController,
-                onDeviceCloseListener,
                 pendingTrampolineCallback,
                 activityListener,
                 soundEffectListener,
                 runningAppsChangedCallback,
-                params);
+                params,
+                DisplayManagerGlobal.getInstance());
     }
 
     @VisibleForTesting
     VirtualDeviceImpl(
             Context context,
             AssociationInfo associationInfo,
+            VirtualDeviceManagerService service,
             IBinder token,
             int ownerUid,
             int deviceId,
             InputController inputController,
             SensorController sensorController,
             CameraAccessController cameraAccessController,
-            OnDeviceCloseListener onDeviceCloseListener,
             PendingTrampolineCallback pendingTrampolineCallback,
             IVirtualDeviceActivityListener activityListener,
             IVirtualDeviceSoundEffectListener soundEffectListener,
             Consumer<ArraySet<Integer>> runningAppsChangedCallback,
-            VirtualDeviceParams params) {
+            VirtualDeviceParams params,
+            DisplayManagerGlobal displayManager) {
         super(PermissionEnforcer.fromContext(context));
         UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid);
         mContext = context.createContextAsUser(ownerUserHandle, 0);
         mAssociationInfo = associationInfo;
+        mService = service;
         mPendingTrampolineCallback = pendingTrampolineCallback;
         mActivityListener = activityListener;
         mSoundEffectListener = soundEffectListener;
@@ -239,6 +248,7 @@
         mDeviceId = deviceId;
         mAppToken = token;
         mParams = params;
+        mDisplayManager = displayManager;
         if (inputController == null) {
             mInputController = new InputController(
                     mVirtualDeviceLock,
@@ -259,7 +269,6 @@
         }
         mCameraAccessController = cameraAccessController;
         mCameraAccessController.startObservingIfNeeded();
-        mOnDeviceCloseListener = onDeviceCloseListener;
         try {
             token.linkToDeath(this, 0);
         } catch (RemoteException e) {
@@ -331,9 +340,11 @@
     @Override // Binder call
     public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
             ResultReceiver resultReceiver) {
-        if (!mVirtualDisplayIds.contains(displayId)) {
-            throw new SecurityException("Display ID " + displayId
-                    + " not found for this virtual device");
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplays.contains(displayId)) {
+                throw new SecurityException("Display ID " + displayId
+                        + " not found for this virtual device");
+            }
         }
         if (pendingIntent.isActivity()) {
             try {
@@ -383,24 +394,34 @@
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void close() {
         super.close_enforcePermission();
+        // Remove about-to-be-closed virtual device from the service before butchering it.
+        mService.removeVirtualDevice(mDeviceId);
+
+        VirtualDisplayWrapper[] virtualDisplaysToBeReleased;
         synchronized (mVirtualDeviceLock) {
-            if (!mPerDisplayWakelocks.isEmpty()) {
-                mPerDisplayWakelocks.forEach((displayId, wakeLock) -> {
-                    Slog.w(TAG, "VirtualDisplay " + displayId + " owned by UID " + mOwnerUid
-                            + " was not properly released");
-                    wakeLock.release();
-                });
-                mPerDisplayWakelocks.clear();
-            }
             if (mVirtualAudioController != null) {
                 mVirtualAudioController.stopListening();
                 mVirtualAudioController = null;
             }
             mLocaleList = null;
+            virtualDisplaysToBeReleased = new VirtualDisplayWrapper[mVirtualDisplays.size()];
+            for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                virtualDisplaysToBeReleased[i] = mVirtualDisplays.valueAt(i);
+            }
+            mVirtualDisplays.clear();
             mVirtualSensorList = null;
             mVirtualSensors.clear();
         }
-        mOnDeviceCloseListener.onClose(mDeviceId);
+        // Destroy the display outside locked section.
+        for (VirtualDisplayWrapper virtualDisplayWrapper : virtualDisplaysToBeReleased) {
+            mDisplayManager.releaseVirtualDisplay(virtualDisplayWrapper.getToken());
+            // The releaseVirtualDisplay call above won't trigger
+            // VirtualDeviceImpl.onVirtualDisplayRemoved callback because we already removed the
+            // virtual device from the service - we release the other display-tied resources here
+            // with the guarantee it will be done exactly once.
+            releaseOwnedVirtualDisplayResources(virtualDisplayWrapper);
+        }
+
         mAppToken.unlinkToDeath(this, 0);
         mCameraAccessController.stopObservingIfNeeded();
 
@@ -429,11 +450,6 @@
         return mVirtualAudioController;
     }
 
-    @VisibleForTesting
-    SparseArray<GenericWindowPolicyController> getWindowPolicyControllersForTesting() {
-        return mWindowPolicyControllers;
-    }
-
     @Override // Binder call
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void onAudioSessionStarting(int displayId,
@@ -441,7 +457,7 @@
             @Nullable IAudioConfigChangedCallback configChangedCallback) {
         super.onAudioSessionStarting_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(displayId)) {
+            if (!mVirtualDisplays.contains(displayId)) {
                 throw new SecurityException(
                         "Cannot start audio session for a display not associated with this virtual "
                                 + "device");
@@ -449,7 +465,8 @@
 
             if (mVirtualAudioController == null) {
                 mVirtualAudioController = new VirtualAudioController(mContext);
-                GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId);
+                GenericWindowPolicyController gwpc = mVirtualDisplays.get(
+                        displayId).getWindowPolicyController();
                 mVirtualAudioController.startListening(gwpc, routingCallback,
                         configChangedCallback);
             }
@@ -473,7 +490,7 @@
     public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualDpad_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+            if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
                         "Cannot create a virtual dpad for a display not associated with "
                                 + "this virtual device");
@@ -493,7 +510,7 @@
     public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualKeyboard_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+            if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
                         "Cannot create a virtual keyboard for a display not associated with "
                                 + "this virtual device");
@@ -515,7 +532,7 @@
     public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualMouse_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+            if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
                         "Cannot create a virtual mouse for a display not associated with this "
                                 + "virtual device");
@@ -536,7 +553,7 @@
             @NonNull IBinder deviceToken) {
         super.createVirtualTouchscreen_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+            if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
                         "Cannot create a virtual touchscreen for a display not associated with "
                                 + "this virtual device");
@@ -566,7 +583,7 @@
             @NonNull IBinder deviceToken) {
         super.createVirtualNavigationTouchpad_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(config.getAssociatedDisplayId())) {
+            if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
                 throw new SecurityException(
                         "Cannot create a virtual navigation touchpad for a display not associated "
                                 + "with this virtual device");
@@ -704,7 +721,8 @@
         try {
             synchronized (mVirtualDeviceLock) {
                 mDefaultShowPointerIcon = showPointerIcon;
-                for (int displayId : mVirtualDisplayIds) {
+                for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                    final int displayId = mVirtualDisplays.keyAt(i);
                     mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
                 }
             }
@@ -795,8 +813,8 @@
         fout.println("    mParams: " + mParams);
         fout.println("    mVirtualDisplayIds: ");
         synchronized (mVirtualDeviceLock) {
-            for (int id : mVirtualDisplayIds) {
-                fout.println("      " + id);
+            for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                fout.println("      " + mVirtualDisplays.keyAt(i));
             }
             fout.println("    mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
         }
@@ -804,61 +822,75 @@
         mSensorController.dump(fout);
     }
 
-    GenericWindowPolicyController createWindowPolicyController(
+    private GenericWindowPolicyController createWindowPolicyController(
             @NonNull List<String> displayCategories) {
-        synchronized (mVirtualDeviceLock) {
-            final GenericWindowPolicyController gwpc =
-                    new GenericWindowPolicyController(FLAG_SECURE,
-                            SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
-                            getAllowedUserHandles(),
-                            mParams.getAllowedCrossTaskNavigations(),
-                            mParams.getBlockedCrossTaskNavigations(),
-                            mParams.getAllowedActivities(),
-                            mParams.getBlockedActivities(),
-                            mParams.getDefaultActivityPolicy(),
-                            createListenerAdapter(),
-                            this::onEnteringPipBlocked,
-                            this::onActivityBlocked,
-                            this::onSecureWindowShown,
-                            this::shouldInterceptIntent,
-                            displayCategories,
-                            mParams.getDefaultRecentsPolicy());
-            gwpc.registerRunningAppsChangedListener(/* listener= */ this);
-            return gwpc;
-        }
+        final GenericWindowPolicyController gwpc =
+                new GenericWindowPolicyController(FLAG_SECURE,
+                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+                        getAllowedUserHandles(),
+                        mParams.getAllowedCrossTaskNavigations(),
+                        mParams.getBlockedCrossTaskNavigations(),
+                        mParams.getAllowedActivities(),
+                        mParams.getBlockedActivities(),
+                        mParams.getDefaultActivityPolicy(),
+                        createListenerAdapter(),
+                        this::onEnteringPipBlocked,
+                        this::onActivityBlocked,
+                        this::onSecureWindowShown,
+                        this::shouldInterceptIntent,
+                        displayCategories,
+                        mParams.getDefaultRecentsPolicy());
+        gwpc.registerRunningAppsChangedListener(/* listener= */ this);
+        return gwpc;
     }
 
-    void onVirtualDisplayCreatedLocked(GenericWindowPolicyController gwpc, int displayId) {
+    int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
+            @NonNull IVirtualDisplayCallback callback, String packageName) {
+        GenericWindowPolicyController gwpc = createWindowPolicyController(
+                virtualDisplayConfig.getDisplayCategories());
+        DisplayManagerInternal displayManager = LocalServices.getService(
+                DisplayManagerInternal.class);
+        int displayId;
+        displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback,
+                this, gwpc, packageName);
+        gwpc.setDisplayId(displayId);
+
         synchronized (mVirtualDeviceLock) {
-            if (displayId == Display.INVALID_DISPLAY) {
-                return;
-            }
-            if (mVirtualDisplayIds.contains(displayId)) {
+            if (mVirtualDisplays.contains(displayId)) {
+                gwpc.unregisterRunningAppsChangedListener(this);
                 throw new IllegalStateException(
                         "Virtual device already has a virtual display with ID " + displayId);
             }
-            mVirtualDisplayIds.add(displayId);
 
-            gwpc.setDisplayId(displayId);
-            mWindowPolicyControllers.put(displayId, gwpc);
+            PowerManager.WakeLock wakeLock = createAndAcquireWakeLockForDisplay(displayId);
+            mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock));
+        }
 
+        final long token = Binder.clearCallingIdentity();
+        try {
             mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
             mInputController.setPointerAcceleration(1f, displayId);
             mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
                     displayId);
             mInputController.setLocalIme(displayId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
 
+        return displayId;
+    }
 
-            if (mPerDisplayWakelocks.containsKey(displayId)) {
-                Slog.e(TAG, "Not creating wakelock for displayId " + displayId);
-                return;
-            }
+    private PowerManager.WakeLock createAndAcquireWakeLockForDisplay(int displayId) {
+        final long token = Binder.clearCallingIdentity();
+        try {
             PowerManager powerManager = mContext.getSystemService(PowerManager.class);
             PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
                     PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
                     TAG + ":" + displayId, displayId);
-            mPerDisplayWakelocks.put(displayId, wakeLock);
             wakeLock.acquire();
+            return wakeLock;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
@@ -872,8 +904,10 @@
     }
 
     private void onSecureWindowShown(int displayId, int uid) {
-        if (!mVirtualDisplayIds.contains(displayId)) {
-            return;
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplays.contains(displayId)) {
+                return;
+            }
         }
 
         // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
@@ -888,55 +922,102 @@
 
     private ArraySet<UserHandle> getAllowedUserHandles() {
         ArraySet<UserHandle> result = new ArraySet<>();
-        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
-        UserManager userManager = mContext.getSystemService(UserManager.class);
-        for (UserHandle profile : userManager.getAllProfiles()) {
-            int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(profile.getIdentifier());
-            if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED
-                    || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) {
-                result.add(profile);
-            } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
-                if (mParams.getUsersWithMatchingAccounts().contains(profile)) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+            UserManager userManager = mContext.getSystemService(UserManager.class);
+            for (UserHandle profile : userManager.getAllProfiles()) {
+                int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(
+                        profile.getIdentifier());
+                if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED
+                        || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) {
                     result.add(profile);
+                } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
+                    if (mParams.getUsersWithMatchingAccounts().contains(profile)) {
+                        result.add(profile);
+                    }
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
         return result;
     }
 
-    void onVirtualDisplayRemovedLocked(int displayId) {
+
+    void onVirtualDisplayRemoved(int displayId) {
+        /* This is callback invoked by VirtualDeviceManagerService when VirtualDisplay was released
+         * by DisplayManager (most probably caused by someone calling VirtualDisplay.close()).
+         * At this point, the display is already released, but we still need to release the
+         * corresponding wakeLock and unregister the RunningAppsChangedListener from corresponding
+         * WindowPolicyController.
+         *
+         * Note that when the display is destroyed during VirtualDeviceImpl.close() call,
+         * this callback won't be invoked because the display is removed from
+         * VirtualDeviceManagerService before any resources are released.
+         */
+        VirtualDisplayWrapper virtualDisplayWrapper;
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplayIds.contains(displayId)) {
-                throw new IllegalStateException(
-                        "Virtual device doesn't have a virtual display with ID " + displayId);
-            }
-            PowerManager.WakeLock wakeLock = mPerDisplayWakelocks.get(displayId);
-            if (wakeLock != null) {
-                wakeLock.release();
-                mPerDisplayWakelocks.remove(displayId);
-            }
-            GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId);
-            if (gwpc != null) {
-                gwpc.unregisterRunningAppsChangedListener(/* listener= */ this);
-            }
-            mVirtualDisplayIds.remove(displayId);
-            mWindowPolicyControllers.remove(displayId);
+            virtualDisplayWrapper = mVirtualDisplays.removeReturnOld(displayId);
         }
+
+        if (virtualDisplayWrapper == null) {
+            throw new IllegalStateException(
+                    "Virtual device doesn't have a virtual display with ID " + displayId);
+        }
+
+        releaseOwnedVirtualDisplayResources(virtualDisplayWrapper);
+
+    }
+
+    /**
+     * Release resources tied to virtual display owned by this VirtualDevice instance.
+     *
+     * Note that this method won't release the virtual display itself.
+     *
+     * @param virtualDisplayWrapper - VirtualDisplayWrapper to release resources for.
+     */
+    private void releaseOwnedVirtualDisplayResources(VirtualDisplayWrapper virtualDisplayWrapper) {
+        virtualDisplayWrapper.getWakeLock().release();
+        virtualDisplayWrapper.getWindowPolicyController().unregisterRunningAppsChangedListener(
+                this);
     }
 
     int getOwnerUid() {
         return mOwnerUid;
     }
 
+    ArraySet<Integer> getDisplayIds() {
+        synchronized (mVirtualDeviceLock) {
+            final int size = mVirtualDisplays.size();
+            ArraySet<Integer> arraySet = new ArraySet<>(size);
+            for (int i = 0; i < size; i++) {
+                arraySet.append(mVirtualDisplays.keyAt(i));
+            }
+            return arraySet;
+        }
+    }
+
+    @VisibleForTesting
+    GenericWindowPolicyController getDisplayWindowPolicyControllerForTest(int displayId) {
+        VirtualDisplayWrapper virtualDisplayWrapper;
+        synchronized (mVirtualDeviceLock) {
+            virtualDisplayWrapper = mVirtualDisplays.get(displayId);
+        }
+        return virtualDisplayWrapper != null ? virtualDisplayWrapper.getWindowPolicyController()
+                : null;
+    }
+
     /**
      * Returns true if an app with the given {@code uid} is currently running on this virtual
      * device.
      */
     boolean isAppRunningOnVirtualDevice(int uid) {
-        final int size = mWindowPolicyControllers.size();
-        for (int i = 0; i < size; i++) {
-            if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
-                return true;
+        synchronized (mVirtualDeviceLock) {
+            for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) {
+                    return true;
+                }
             }
         }
         return false;
@@ -957,11 +1038,9 @@
             Looper looper) {
         synchronized (mVirtualDeviceLock) {
             DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
-            final int size = mWindowPolicyControllers.size();
-            for (int i = 0; i < size; i++) {
-                if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
-                    int displayId = mWindowPolicyControllers.keyAt(i);
-                    Display display = displayManager.getDisplay(displayId);
+            for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) {
+                    Display display = displayManager.getDisplay(mVirtualDisplays.keyAt(i));
                     if (display != null && display.isValid()) {
                         Toast.makeText(mContext.createDisplayContext(display), looper, text,
                                 duration).show();
@@ -972,7 +1051,9 @@
     }
 
     boolean isDisplayOwnedByVirtualDevice(int displayId) {
-        return mVirtualDisplayIds.contains(displayId);
+        synchronized (mVirtualDeviceLock) {
+            return mVirtualDisplays.contains(displayId);
+        }
     }
 
     void onEnteringPipBlocked(int uid) {
@@ -1016,10 +1097,6 @@
         }
     }
 
-    interface OnDeviceCloseListener {
-        void onClose(int deviceId);
-    }
-
     interface PendingTrampolineCallback {
         /**
          * Called when the callback should start waiting for the given pending trampoline.
@@ -1073,4 +1150,31 @@
                     + ", displayId=" + mDisplayId + "}";
         }
     }
+
+    /** Data class wrapping resources tied to single virtual display. */
+    private static final class VirtualDisplayWrapper {
+        private final IVirtualDisplayCallback mToken;
+        private final GenericWindowPolicyController mWindowPolicyController;
+        private final PowerManager.WakeLock mWakeLock;
+
+        VirtualDisplayWrapper(@NonNull IVirtualDisplayCallback token,
+                @NonNull GenericWindowPolicyController windowPolicyController,
+                @NonNull PowerManager.WakeLock wakeLock) {
+            mToken = Objects.requireNonNull(token);
+            mWindowPolicyController = Objects.requireNonNull(windowPolicyController);
+            mWakeLock = Objects.requireNonNull(wakeLock);
+        }
+
+        GenericWindowPolicyController getWindowPolicyController() {
+            return mWindowPolicyController;
+        }
+
+        PowerManager.WakeLock getWakeLock() {
+            return mWakeLock;
+        }
+
+        IVirtualDisplayCallback getToken() {
+            return mToken;
+        }
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 9bb05a6..ed5b858 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -37,7 +37,6 @@
 import android.companion.virtual.sensor.VirtualSensor;
 import android.content.Context;
 import android.content.Intent;
-import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
 import android.os.Binder;
@@ -108,26 +107,26 @@
     private final ActivityInterceptorCallback mActivityInterceptorCallback =
             new ActivityInterceptorCallback() {
 
-        @Nullable
-        @Override
-        public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
-                ActivityInterceptorInfo info) {
-            if (info.getCallingPackage() == null) {
-                return null;
-            }
-            PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage());
-            if (pt == null) {
-                return null;
-            }
-            pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null);
-            ActivityOptions options = info.getCheckedOptions();
-            if (options == null) {
-                options = ActivityOptions.makeBasic();
-            }
-            return new ActivityInterceptResult(
-                    info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId));
-        }
-    };
+                @Nullable
+                @Override
+                public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
+                        ActivityInterceptorInfo info) {
+                    if (info.getCallingPackage() == null) {
+                        return null;
+                    }
+                    PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage());
+                    if (pt == null) {
+                        return null;
+                    }
+                    pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null);
+                    ActivityOptions options = info.getCheckedOptions();
+                    if (options == null) {
+                        options = ActivityOptions.makeBasic();
+                    }
+                    return new ActivityInterceptResult(
+                            info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId));
+                }
+            };
 
     @Override
     public void onStart() {
@@ -146,8 +145,8 @@
                 CharSequence deviceName = mVirtualDevices.valueAt(i).getDisplayName();
                 mVirtualDevices.valueAt(i).showToastWhereUidIsRunning(appUid,
                         getContext().getString(
-                            com.android.internal.R.string.vdm_camera_access_denied,
-                            deviceName),
+                                com.android.internal.R.string.vdm_camera_access_denied,
+                                deviceName),
                         Toast.LENGTH_LONG, Looper.myLooper());
             }
         }
@@ -193,34 +192,46 @@
         }
     }
 
-    @VisibleForTesting
     void removeVirtualDevice(int deviceId) {
         synchronized (mVirtualDeviceManagerLock) {
             mAppsOnVirtualDevices.remove(deviceId);
             mVirtualDevices.remove(deviceId);
         }
+
+        Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED);
+        i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId);
+        i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            getContext().sendBroadcastAsUser(i, UserHandle.ALL);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
 
         private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback =
                 new VirtualDeviceImpl.PendingTrampolineCallback() {
-            @Override
-            public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
-                PendingTrampoline existing = mPendingTrampolines.put(
-                        pendingTrampoline.mPendingIntent.getCreatorPackage(),
-                        pendingTrampoline);
-                if (existing != null) {
-                    existing.mResultReceiver.send(
-                            VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null);
-                }
-            }
+                    @Override
+                    public void startWaitingForPendingTrampoline(
+                            PendingTrampoline pendingTrampoline) {
+                        PendingTrampoline existing = mPendingTrampolines.put(
+                                pendingTrampoline.mPendingIntent.getCreatorPackage(),
+                                pendingTrampoline);
+                        if (existing != null) {
+                            existing.mResultReceiver.send(
+                                    VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null);
+                        }
+                    }
 
-            @Override
-            public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
-                mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage());
-            }
-        };
+                    @Override
+                    public void stopWaitingForPendingTrampoline(
+                            PendingTrampoline pendingTrampoline) {
+                        mPendingTrampolines.remove(
+                                pendingTrampoline.mPendingIntent.getCreatorPackage());
+                    }
+                };
 
         @Override // Binder call
         public IVirtualDevice createVirtualDevice(
@@ -251,8 +262,9 @@
                 final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
                         runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
                 VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
-                        associationInfo, token, callingUid, deviceId, cameraAccessController,
-                        this::onDeviceClosed, mPendingTrampolineCallback, activityListener,
+                        associationInfo, VirtualDeviceManagerService.this, token, callingUid,
+                        deviceId, cameraAccessController,
+                        mPendingTrampolineCallback, activityListener,
                         soundEffectListener, runningAppsChangedCallback, params);
                 mVirtualDevices.put(deviceId, virtualDevice);
                 return virtualDevice;
@@ -281,26 +293,9 @@
                         "uid " + callingUid
                                 + " is not the owner of the supplied VirtualDevice");
             }
-            GenericWindowPolicyController gwpc;
-            final long token = Binder.clearCallingIdentity();
-            try {
-                gwpc = virtualDeviceImpl.createWindowPolicyController(
-                    virtualDisplayConfig.getDisplayCategories());
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
 
-            DisplayManagerInternal displayManager = getLocalService(
-                    DisplayManagerInternal.class);
-            int displayId = displayManager.createVirtualDisplay(virtualDisplayConfig, callback,
-                    virtualDevice, gwpc, packageName);
-
-            final long tokenTwo = Binder.clearCallingIdentity();
-            try {
-                virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
-            } finally {
-                Binder.restoreCallingIdentity(tokenTwo);
-            }
+            int displayId = virtualDeviceImpl.createVirtualDisplay(virtualDisplayConfig, callback,
+                    packageName);
             mLocalService.onVirtualDisplayCreated(displayId);
             return displayId;
         }
@@ -412,19 +407,6 @@
             return null;
         }
 
-        private void onDeviceClosed(int deviceId) {
-            removeVirtualDevice(deviceId);
-            Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED);
-            i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId);
-            i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                getContext().sendBroadcastAsUser(i, UserHandle.ALL);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
         @Override
         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                 throws RemoteException {
@@ -512,9 +494,14 @@
         @Override
         public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
             final VirtualDisplayListener[] listeners;
+            VirtualDeviceImpl virtualDeviceImpl;
             synchronized (mVirtualDeviceManagerLock) {
-                ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
                 listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+                virtualDeviceImpl = mVirtualDevices.get(
+                        ((VirtualDeviceImpl) virtualDevice).getDeviceId());
+            }
+            if (virtualDeviceImpl != null) {
+                virtualDeviceImpl.onVirtualDisplayRemoved(displayId);
             }
             mHandler.post(() -> {
                 for (VirtualDisplayListener listener : listeners) {
@@ -599,16 +586,11 @@
 
         @Override
         public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) {
+            VirtualDeviceImpl virtualDevice;
             synchronized (mVirtualDeviceManagerLock) {
-                int size = mVirtualDevices.size();
-                for (int i = 0; i < size; i++) {
-                    VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
-                    if (device.getDeviceId() == deviceId) {
-                        return new ArraySet<>(device.mVirtualDisplayIds);
-                    }
-                }
+                virtualDevice = mVirtualDevices.get(deviceId);
             }
-            return new ArraySet<>();
+            return virtualDevice == null ? new ArraySet<>() : virtualDevice.getDisplayIds();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0252492..e0d1c1e 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2753,6 +2753,8 @@
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
 
         try {
+            int avgWriteAmount = 0;
+            int targetDirtyRatio = mTargetDirtyRatio;
             int latestWrite = mVold.getWriteAmount();
             if (latestWrite == -1) {
                 Slog.w(TAG, "Failed to get storage write record");
@@ -2765,11 +2767,12 @@
             // (first boot after OTA), We skip the smart idle maintenance
             if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
                 if (!refreshLifetimeConstraint() || !checkChargeStatus()) {
-                    return;
+                    Slog.i(TAG, "Turn off gc_urgent based on checking lifetime and charge status");
+                    targetDirtyRatio = 100;
+                } else {
+                    avgWriteAmount = getAverageWriteAmount();
                 }
 
-                int avgWriteAmount = getAverageWriteAmount();
-
                 Slog.i(TAG, "Set smart idle maintenance: " + "latest write amount: " +
                             latestWrite + ", average write amount: " + avgWriteAmount +
                             ", min segment threshold: " + mMinSegmentsThreshold +
@@ -2777,10 +2780,10 @@
                             ", segment reclaim weight: " + mSegmentReclaimWeight +
                             ", period(min): " + sSmartIdleMaintPeriod +
                             ", min gc sleep time(ms): " + mMinGCSleepTime +
-                            ", target dirty ratio: " + mTargetDirtyRatio);
+                            ", target dirty ratio: " + targetDirtyRatio);
                 mVold.setGCUrgentPace(avgWriteAmount, mMinSegmentsThreshold, mDirtyReclaimRate,
                                       mSegmentReclaimWeight, sSmartIdleMaintPeriod,
-                                      mMinGCSleepTime, mTargetDirtyRatio);
+                                      mMinGCSleepTime, targetDirtyRatio);
             } else {
                 Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d5d0edc..70304c5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -522,6 +522,7 @@
         final ArrayList<ServiceRecord> mStartingBackground = new ArrayList<>();
 
         final ArrayMap<String, ActiveForegroundApp> mActiveForegroundApps = new ArrayMap<>();
+        final ArrayList<String> mPendingRemoveForegroundApps = new ArrayList<>();
 
         boolean mActiveForegroundAppsChanged;
 
@@ -1687,13 +1688,14 @@
             if (smap != null) {
                 if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Updating foreground apps for user "
                         + smap.mUserId);
+                smap.mPendingRemoveForegroundApps.clear();
                 for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
                     ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i);
                     if (aa.mEndTime != 0) {
                         boolean canRemove = foregroundAppShownEnoughLocked(aa, now);
                         if (canRemove) {
                             // This was up for longer than the timeout, so just remove immediately.
-                            smap.mActiveForegroundApps.removeAt(i);
+                            smap.mPendingRemoveForegroundApps.add(smap.mActiveForegroundApps.keyAt(i));
                             smap.mActiveForegroundAppsChanged = true;
                             continue;
                         }
@@ -1718,6 +1720,9 @@
                         }
                     }
                 }
+                for(int i = smap.mPendingRemoveForegroundApps.size() - 1; i >= 0; i--) {
+                    smap.mActiveForegroundApps.remove(smap.mPendingRemoveForegroundApps.get(i));
+                }
                 smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
                 if (nextUpdateTime < Long.MAX_VALUE) {
                     if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Next update time in: "
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 6619025..c2326f6 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -1388,7 +1388,7 @@
         final int mDefaultBgCurrentDrainPowerComponent;
 
         /**
-         * Default value to {@link #mBgCurrentDrainExmptedTypes}.
+         * Default value to {@link #mBgCurrentDrainExemptedTypes}.
          **/
         final int mDefaultBgCurrentDrainExemptedTypes;
 
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index c0cb7d9..993595b 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -316,6 +316,10 @@
         // then we should care, otherwise we assume
         // it's not related to any FGS
         UidState uidState = mUids.get(uid);
+        if (uidState == null) {
+            Log.w(TAG, "API event end called before start!");
+            return -1;
+        }
         if (uidState.mOpenWithFgsCount.contains(apiType)) {
             // are there any calls that started with an FGS?
             if (uidState.mOpenWithFgsCount.get(apiType) != 0) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index dce88da..005ad20 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -23,6 +23,7 @@
 import android.app.TaskStackListener;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.hardware.biometrics.AuthenticateOptions;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
@@ -44,8 +45,8 @@
 /**
  * A class to keep track of the authentication state for a given client.
  */
-public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
-        implements AuthenticationConsumer {
+public abstract class AuthenticationClient<T, O extends AuthenticateOptions>
+        extends AcquisitionClient<T> implements AuthenticationConsumer {
 
     // New, has not started yet
     public static final int STATE_NEW = 0;
@@ -89,14 +90,15 @@
 
     public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int targetUserId, long operationId, boolean restricted, @NonNull String owner,
-            int cookie, boolean requireConfirmation, int sensorId,
+            long operationId, boolean restricted, @NonNull O options,
+            int cookie, boolean requireConfirmation,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener,
             @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
             boolean shouldVibrate, int sensorStrength) {
-        super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
-                shouldVibrate, biometricLogger, biometricContext);
+        super(context, lazyDaemon, token, listener, options.getUserId(),
+                options.getOpPackageName(), cookie, options.getSensorId(), shouldVibrate,
+                biometricLogger, biometricContext);
         mIsStrongBiometric = isStrongBiometric;
         mOperationId = operationId;
         mRequireConfirmation = requireConfirmation;
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
index 0f1fe68..25651fa28 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.util.proto.ProtoOutputStream;
 
@@ -39,7 +40,7 @@
     List<T> getSensorProperties();
 
     /** Properties for the given sensor id. */
-    @NonNull
+    @Nullable
     T getSensorProperties(int sensorId);
 
     boolean isHardwareDetected(int sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index 2263e80..a4b0a0e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -92,7 +92,7 @@
         final int previousBiometricState = mBiometricState;
 
         if (client instanceof AuthenticationClient) {
-            final AuthenticationClient<?> authClient = (AuthenticationClient<?>) client;
+            final AuthenticationClient<?, ?> authClient = (AuthenticationClient<?, ?>) client;
             if (authClient.isKeyguard()) {
                 mBiometricState = STATE_KEYGUARD_AUTH;
             } else if (authClient.isBiometricPrompt()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 5182968..fb64bcc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -64,9 +64,10 @@
             long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
             String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
             throws RemoteException {
-        mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId,
+        mFaceService.prepareForAuthentication(requireConfirmation, token, operationId,
                 sensorReceiver, new FaceAuthenticateOptions.Builder()
                         .setUserId(userId)
+                        .setSensorId(mSensorId)
                         .setOpPackageName(opPackageName)
                         .build(),
                 requestId, cookie, allowBackgroundAuthentication);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 1ee9f53..6d7b2cb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -246,7 +246,6 @@
 
             super.authenticate_enforcePermission();
 
-            final int userId = options.getUserId();
             final String opPackageName = options.getOpPackageName();
             final boolean restricted = false; // Face APIs are private
             final int statsClient = Utils.isKeyguard(getContext(), opPackageName)
@@ -261,9 +260,11 @@
             if (provider == null) {
                 Slog.w(TAG, "Null provider for authenticate");
                 return -1;
+            } else {
+                options.setSensorId(provider.first);
             }
 
-            return provider.second.scheduleAuthenticate(provider.first, token, operationId,
+            return provider.second.scheduleAuthenticate(token, operationId,
                     0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options,
                     restricted, statsClient, isKeyguard);
         }
@@ -286,28 +287,27 @@
                 return -1;
             }
 
-            return provider.second.scheduleFaceDetect(provider.first, token, options.getUserId(),
-                    new ClientMonitorCallbackConverter(receiver), opPackageName,
+            return provider.second.scheduleFaceDetect(token,
+                    new ClientMonitorCallbackConverter(receiver), options,
                     BiometricsProtoEnums.CLIENT_KEYGUARD);
         }
 
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override // Binder call
-        public void prepareForAuthentication(int sensorId, boolean requireConfirmation,
+        public void prepareForAuthentication(boolean requireConfirmation,
                 IBinder token, long operationId, IBiometricSensorReceiver sensorReceiver,
                 FaceAuthenticateOptions options, long requestId, int cookie,
                 boolean allowBackgroundAuthentication) {
             super.prepareForAuthentication_enforcePermission();
 
-            final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
+            final ServiceProvider provider = mRegistry.getProviderForSensor(options.getSensorId());
             if (provider == null) {
                 Slog.w(TAG, "Null provider for prepareForAuthentication");
                 return;
             }
 
-            final boolean isKeyguardBypassEnabled = false; // only valid for keyguard clients
             final boolean restricted = true; // BiometricPrompt is always restricted
-            provider.scheduleAuthenticate(sensorId, token, operationId, cookie,
+            provider.scheduleAuthenticate(token, operationId, cookie,
                     new ClientMonitorCallbackConverter(sensorReceiver), options, requestId,
                     restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                     allowBackgroundAuthentication);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 609c6a7..2cf64b7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -83,18 +83,19 @@
 
     void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
 
-    long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
-            @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+    long scheduleFaceDetect(@NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FaceAuthenticateOptions options,
             int statsClient);
 
     void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId);
 
-    long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+    long scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull FaceAuthenticateOptions options,
             boolean restricted, int statsClient, boolean allowBackgroundAuthentication);
 
-    void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+    void scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull FaceAuthenticateOptions options, long requestId,
             boolean restricted, int statsClient, boolean allowBackgroundAuthentication);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 29dd707..976f1cb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -28,6 +28,7 @@
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.face.IFace;
+import android.hardware.face.FaceAuthenticateOptions;
 import android.hardware.face.FaceAuthenticationFrame;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
@@ -56,7 +57,7 @@
 /**
  * Face-specific authentication client for the {@link IFace} AIDL HAL interface.
  */
-class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
+class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAuthenticateOptions>
         implements LockoutConsumer {
     private static final String TAG = "FaceAuthenticationClient";
 
@@ -80,16 +81,16 @@
     FaceAuthenticationClient(@NonNull Context context,
             @NonNull Supplier<AidlSession> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
-            boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener, long operationId,
+            boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
+            boolean requireConfirmation,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric, @NonNull UsageStats usageStats,
             @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
             @Authenticators.Types int sensorStrength) {
-        this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId,
-                restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
-                isStrongBiometric, usageStats, lockoutCache /* lockoutCache */,
-                allowBackgroundAuthentication,
+        this(context, lazyDaemon, token, requestId, listener, operationId,
+                restricted, options, cookie, requireConfirmation, logger, biometricContext,
+                isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication,
                 context.getSystemService(SensorPrivacyManager.class), sensorStrength);
     }
 
@@ -97,15 +98,16 @@
     FaceAuthenticationClient(@NonNull Context context,
             @NonNull Supplier<AidlSession> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
-            boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener, long operationId,
+            boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
+            boolean requireConfirmation,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric, @NonNull UsageStats usageStats,
             @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
             SensorPrivacyManager sensorPrivacyManager,
             @Authenticators.Types int biometricStrength) {
-        super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
-                owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+        super(context, lazyDaemon, token, listener, operationId, restricted,
+                options, cookie, requireConfirmation, logger, biometricContext,
                 isStrongBiometric, null /* taskStackListener */, null /* lockoutCache */,
                 allowBackgroundAuthentication, false /* shouldVibrate */,
                 biometricStrength);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 506b2bc..e65202d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -22,6 +22,7 @@
 import android.hardware.SensorPrivacyManager;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.face.FaceAuthenticateOptions;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -51,11 +52,11 @@
 
     FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull String owner, int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FaceAuthenticateOptions options,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric) {
-        this(context, lazyDaemon, token, requestId, listener, userId, owner, sensorId,
+        this(context, lazyDaemon, token, requestId, listener, options,
                 logger, biometricContext, isStrongBiometric,
                 context.getSystemService(SensorPrivacyManager.class));
     }
@@ -63,11 +64,12 @@
     @VisibleForTesting
     FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull String owner, int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FaceAuthenticateOptions options,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
-        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+        super(context, lazyDaemon, token, listener, options.getUserId(),
+                options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
                 true /* shouldVibrate */, logger, biometricContext);
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 41e0269..cf8ea86 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -410,16 +410,17 @@
     }
 
     @Override
-    public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
-            int userId, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, int statsClient) {
+    public long scheduleFaceDetect(@NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FaceAuthenticateOptions options, int statsClient) {
         final long id = mRequestCounter.incrementAndGet();
+        final int sensorId = options.getSensorId();
 
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FaceDetectClient client = new FaceDetectClient(mContext,
                     mSensors.get(sensorId).getLazySession(),
-                    token, id, callback, userId, opPackageName, sensorId,
+                    token, id, callback, options,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric);
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
@@ -435,18 +436,19 @@
     }
 
     @Override
-    public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+    public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull FaceAuthenticateOptions options,
             long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         mHandler.post(() -> {
             final int userId = options.getUserId();
+            final int sensorId = options.getSensorId();
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FaceAuthenticationClient client = new FaceAuthenticationClient(
                     mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
-                    userId, operationId, restricted, options.getOpPackageName(), cookie,
-                    false /* requireConfirmation */, sensorId,
+                    operationId, restricted, options, cookie,
+                    false /* requireConfirmation */,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric,
                     mUsageStats, mSensors.get(sensorId).getLockoutCache(),
@@ -470,13 +472,13 @@
     }
 
     @Override
-    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+    public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         final long id = mRequestCounter.incrementAndGet();
 
-        scheduleAuthenticate(sensorId, token, operationId, cookie, callback,
+        scheduleAuthenticate(token, operationId, cookie, callback,
                 options, id, restricted, statsClient, allowBackgroundAuthentication);
 
         return id;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 7e575bc..0f22296 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -651,9 +651,9 @@
     }
 
     @Override
-    public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
-            int userId, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, int statsClient) {
+    public long scheduleFaceDetect(@NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FaceAuthenticateOptions options, int statsClient) {
         throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
                 + "forget to check the supportsFaceDetection flag?");
     }
@@ -665,7 +665,7 @@
     }
 
     @Override
-    public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+    public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter receiver,
             @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
             int statsClient, boolean allowBackgroundAuthentication) {
@@ -675,8 +675,8 @@
 
             final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
             final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
-                    mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
-                    options.getOpPackageName(), cookie, false /* requireConfirmation */, mSensorId,
+                    mLazyDaemon, token, requestId, receiver, operationId, restricted,
+                    options, cookie, false /* requireConfirmation */,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric, mLockoutTracker,
                     mUsageStats, allowBackgroundAuthentication,
@@ -686,13 +686,13 @@
     }
 
     @Override
-    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+    public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter receiver,
             @NonNull FaceAuthenticateOptions options, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         final long id = mRequestCounter.incrementAndGet();
 
-        scheduleAuthenticate(sensorId, token, operationId, cookie, receiver,
+        scheduleAuthenticate(token, operationId, cookie, receiver,
                 options, id, restricted, statsClient, allowBackgroundAuthentication);
 
         return id;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 1c1f56c..8ab8892 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -25,6 +25,7 @@
 import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceAuthenticateOptions;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -50,7 +51,8 @@
  * Face-specific authentication client supporting the {@link android.hardware.biometrics.face.V1_0}
  * HIDL interface.
  */
-class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
+class FaceAuthenticationClient
+        extends AuthenticationClient<IBiometricsFace, FaceAuthenticateOptions> {
 
     private static final String TAG = "FaceAuthenticationClient";
 
@@ -67,17 +69,18 @@
     FaceAuthenticationClient(@NonNull Context context,
             @NonNull Supplier<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
-            boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener, long operationId,
+            boolean restricted, @NonNull FaceAuthenticateOptions options, int cookie,
+            boolean requireConfirmation,
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker,
             @NonNull UsageStats usageStats, boolean allowBackgroundAuthentication,
             @Authenticators.Types int sensorStrength) {
-        super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
-                owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+        super(context, lazyDaemon, token, listener, operationId, restricted,
+                options, cookie, requireConfirmation, logger, biometricContext,
                 isStrongBiometric, null /* taskStackListener */,
                 lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */,
-                 sensorStrength);
+                sensorStrength);
         setRequestId(requestId);
         mUsageStats = usageStats;
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 52d887a..d47a57a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -23,6 +23,7 @@
 import android.hardware.biometrics.ITestSession;
 import android.hardware.biometrics.ITestSessionCallback;
 import android.hardware.biometrics.SensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.IFingerprintService;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -63,8 +64,13 @@
             long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
             String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
             throws RemoteException {
-        mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId,
-                sensorReceiver, opPackageName, requestId, cookie, allowBackgroundAuthentication);
+        mFingerprintService.prepareForAuthentication(token, operationId, sensorReceiver,
+                new FingerprintAuthenticateOptions.Builder()
+                        .setSensorId(mSensorId)
+                        .setUserId(userId)
+                        .setOpPackageName(opPackageName)
+                        .build(),
+                requestId, cookie, allowBackgroundAuthentication);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index affc496e..8a33f22 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -119,7 +119,7 @@
     @NonNull
     private final Supplier<String[]> mAidlInstanceNameSupplier;
     @NonNull
-    private final Function<String, IFingerprint> mIFingerprintProvider;
+    private final Function<String, FingerprintProvider> mFingerprintProvider;
     @NonNull
     private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
             mBiometricStateCallback;
@@ -307,6 +307,8 @@
             if (provider == null) {
                 Slog.w(TAG, "Null provider for authenticate");
                 return -1;
+            } else {
+                options.setSensorId(provider.first);
             }
 
             final FingerprintSensorPropertiesInternal sensorProps =
@@ -322,8 +324,8 @@
                     return -1;
                 }
             }
-            return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
-                    0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+            return provider.second.scheduleAuthenticate(token, operationId,
+                    0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options,
                     restricted, statsClient, isKeyguard);
         }
 
@@ -436,29 +438,32 @@
             if (provider == null) {
                 Slog.w(TAG, "Null provider for detectFingerprint");
                 return -1;
+            } else {
+                options.setSensorId(provider.first);
             }
 
-            return provider.second.scheduleFingerDetect(provider.first, token, options.getUserId(),
-                    new ClientMonitorCallbackConverter(receiver), opPackageName,
+            return provider.second.scheduleFingerDetect(token,
+                    new ClientMonitorCallbackConverter(receiver), options,
                     BiometricsProtoEnums.CLIENT_KEYGUARD);
         }
 
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
         @Override // Binder call
-        public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
-                int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
+        public void prepareForAuthentication(IBinder token, long operationId,
+                IBiometricSensorReceiver sensorReceiver,
+                @NonNull FingerprintAuthenticateOptions options,
                 long requestId, int cookie, boolean allowBackgroundAuthentication) {
             super.prepareForAuthentication_enforcePermission();
 
-            final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
+            final ServiceProvider provider = mRegistry.getProviderForSensor(options.getSensorId());
             if (provider == null) {
                 Slog.w(TAG, "Null provider for prepareForAuthentication");
                 return;
             }
 
             final boolean restricted = true; // BiometricPrompt is always restricted
-            provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
-                    new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
+            provider.scheduleAuthenticate(token, operationId, cookie,
+                    new ClientMonitorCallbackConverter(sensorReceiver), options, requestId,
                     restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                     allowBackgroundAuthentication);
         }
@@ -982,8 +987,7 @@
                 () -> IBiometricService.Stub.asInterface(
                         ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
                 () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
-                (fqName) -> IFingerprint.Stub.asInterface(
-                        Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName))));
+                null /* fingerprintProvider */);
     }
 
     @VisibleForTesting
@@ -991,16 +995,35 @@
             BiometricContext biometricContext,
             Supplier<IBiometricService> biometricServiceSupplier,
             Supplier<String[]> aidlInstanceNameSupplier,
-            Function<String, IFingerprint> fingerprintProvider) {
+            Function<String, FingerprintProvider> fingerprintProvider) {
         super(context);
         mBiometricContext = biometricContext;
         mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
-        mIFingerprintProvider = fingerprintProvider;
         mAppOps = context.getSystemService(AppOpsManager.class);
         mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
         mLockoutResetDispatcher = new LockoutResetDispatcher(context);
         mLockPatternUtils = new LockPatternUtils(context);
         mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
+        mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider :
+                (name) -> {
+                    final String fqName = IFingerprint.DESCRIPTOR + "/" + name;
+                    final IFingerprint fp = IFingerprint.Stub.asInterface(
+                            Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+                    if (fp != null) {
+                        try {
+                            return new FingerprintProvider(getContext(),
+                                    mBiometricStateCallback, fp.getSensorProps(), name,
+                                    mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
+                                    mBiometricContext);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
+                        }
+                    } else {
+                        Slog.e(TAG, "Unable to get declared service: " + fqName);
+                    }
+
+                    return null;
+                };
         mHandler = new Handler(Looper.getMainLooper());
         mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
         mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -1044,23 +1067,9 @@
         final List<ServiceProvider> providers = new ArrayList<>();
 
         for (String instance : instances) {
-            final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
-            final IFingerprint fp = mIFingerprintProvider.apply(fqName);
-
-            if (fp != null) {
-                try {
-                    final FingerprintProvider provider = new FingerprintProvider(getContext(),
-                            mBiometricStateCallback, fp.getSensorProps(), instance,
-                            mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
-                            mBiometricContext);
-                    Slog.i(TAG, "Adding AIDL provider: " + fqName);
-                    providers.add(provider);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
-                }
-            } else {
-                Slog.e(TAG, "Unable to get declared service: " + fqName);
-            }
+            final FingerprintProvider provider = mFingerprintProvider.apply(instance);
+            Slog.i(TAG, "Adding AIDL provider: " + instance);
+            providers.add(provider);
         }
 
         return providers;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 5b6f14d..004af2c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -23,6 +23,7 @@
 import android.hardware.biometrics.ITestSessionCallback;
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -78,19 +79,21 @@
 
     void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
 
-    long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
-            @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+    long scheduleFingerDetect(@NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FingerprintAuthenticateOptions options,
             int statsClient);
 
-    void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+    void scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+            @NonNull FingerprintAuthenticateOptions options,
+            long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication);
 
-    long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+    long scheduleAuthenticate(@NonNull IBinder token, long operationId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication);
+            @NonNull FingerprintAuthenticateOptions options,
+            boolean restricted, int statsClient, boolean allowBackgroundAuthentication);
 
     void startPreparedClient(int sensorId, int cookie);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index d1a7b13..0f81f9f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -26,6 +26,7 @@
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlay;
@@ -65,7 +66,8 @@
  * Fingerprint-specific authentication client supporting the {@link
  * android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
  */
-class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
+class FingerprintAuthenticationClient
+        extends AuthenticationClient<AidlSession, FingerprintAuthenticateOptions>
         implements Udfps, LockoutConsumer, PowerPressHandler {
     private static final String TAG = "FingerprintAuthenticationClient";
     private static final int MESSAGE_AUTH_SUCCESS = 2;
@@ -97,13 +99,11 @@
             @NonNull IBinder token,
             long requestId,
             @NonNull ClientMonitorCallbackConverter listener,
-            int targetUserId,
             long operationId,
             boolean restricted,
-            @NonNull String owner,
+            @NonNull FingerprintAuthenticateOptions options,
             int cookie,
             boolean requireConfirmation,
-            int sensorId,
             @NonNull BiometricLogger biometricLogger,
             @NonNull BiometricContext biometricContext,
             boolean isStrongBiometric,
@@ -122,13 +122,11 @@
                 lazyDaemon,
                 token,
                 listener,
-                targetUserId,
                 operationId,
                 restricted,
-                owner,
+                options,
                 cookie,
                 requireConfirmation,
-                sensorId,
                 biometricLogger,
                 biometricContext,
                 isStrongBiometric,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index f6911ea..376d231 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
@@ -52,13 +53,14 @@
 
     FingerprintDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull String owner, int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FingerprintAuthenticateOptions options,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
             @Nullable IUdfpsOverlay udfpsOverlay,
             boolean isStrongBiometric) {
-        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+        super(context, lazyDaemon, token, listener, options.getUserId(),
+                options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
                 true /* shouldVibrate */, biometricLogger, biometricContext);
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 776d331..a833278 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -37,6 +37,7 @@
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -422,15 +423,17 @@
     }
 
     @Override
-    public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
-            @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+    public long scheduleFingerDetect(@NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FingerprintAuthenticateOptions options,
             int statsClient) {
         final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
+            final int sensorId = options.getSensorId();
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
-                    mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
-                    opPackageName, sensorId,
+                    mSensors.get(sensorId).getLazySession(), token, id, callback,
+                    options,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext,
                     mUdfpsOverlayController, mUdfpsOverlay, isStrongBiometric);
@@ -441,16 +444,19 @@
     }
 
     @Override
-    public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
-            int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+    public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
+            int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FingerprintAuthenticateOptions options,
+            long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         mHandler.post(() -> {
+            final int userId = options.getUserId();
+            final int sensorId = options.getSensorId();
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
                     mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
-                    userId, operationId, restricted, opPackageName, cookie,
-                    false /* requireConfirmation */, sensorId,
+                    operationId, restricted, options, cookie,
+                    false /* requireConfirmation */,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric,
                     mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
@@ -485,14 +491,14 @@
     }
 
     @Override
-    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
-            int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
+    public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
+            int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull FingerprintAuthenticateOptions options, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         final long id = mRequestCounter.incrementAndGet();
 
-        scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
-                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
+        scheduleAuthenticate(token, operationId, cookie, callback,
+                options, id, restricted, statsClient, allowBackgroundAuthentication);
 
         return id;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 4567addc..99c491a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -34,6 +34,7 @@
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -631,17 +632,17 @@
     }
 
     @Override
-    public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
-            @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
+    public long scheduleFingerDetect(@NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FingerprintAuthenticateOptions options,
             int statsClient) {
         final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
-            scheduleUpdateActiveUserWithoutHandler(userId);
+            scheduleUpdateActiveUserWithoutHandler(options.getUserId());
 
             final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
             final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
-                    mLazyDaemon, token, id, listener, userId, opPackageName,
-                    mSensorProperties.sensorId,
+                    mLazyDaemon, token, id, listener, options,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, mUdfpsOverlayController, mUdfpsOverlay,
                     isStrongBiometric);
@@ -652,18 +653,18 @@
     }
 
     @Override
-    public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
-            int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
-            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+    public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
+            int cookie, @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FingerprintAuthenticateOptions options,
+            long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         mHandler.post(() -> {
-            scheduleUpdateActiveUserWithoutHandler(userId);
+            scheduleUpdateActiveUserWithoutHandler(options.getUserId());
 
             final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
             final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
-                    mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
-                    restricted, opPackageName, cookie, false /* requireConfirmation */,
-                    mSensorProperties.sensorId,
+                    mContext, mLazyDaemon, token, requestId, listener, operationId,
+                    restricted, options, cookie, false /* requireConfirmation */,
                     createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
                     mBiometricContext, isStrongBiometric,
                     mTaskStackListener, mLockoutTracker,
@@ -675,14 +676,14 @@
     }
 
     @Override
-    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
-            int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
+    public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
+            int cookie, @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FingerprintAuthenticateOptions options, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication) {
         final long id = mRequestCounter.incrementAndGet();
 
-        scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
-                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
+        scheduleAuthenticate(token, operationId, cookie, listener,
+                options, id, restricted, statsClient, allowBackgroundAuthentication);
 
         return id;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 73b1288..0a47c12 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -22,6 +22,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -362,13 +363,16 @@
             // Store the authClient parameters so it can be rescheduled
             final IBinder token = client.getToken();
             final long operationId = authClient.getOperationId();
-            final int user = client.getTargetUserId();
             final int cookie = client.getCookie();
             final ClientMonitorCallbackConverter listener = client.getListener();
-            final String opPackageName = client.getOwnerString();
             final boolean restricted = authClient.isRestricted();
             final int statsClient = client.getLogger().getStatsClient();
             final boolean isKeyguard = authClient.isKeyguard();
+            final FingerprintAuthenticateOptions options =
+                    new FingerprintAuthenticateOptions.Builder()
+                            .setUserId(client.getTargetUserId())
+                            .setOpPackageName(client.getOwnerString())
+                            .build();
 
             // Don't actually send cancel() to the HAL, since successful auth already finishes
             // HAL authenticate() lifecycle. Just
@@ -376,8 +380,8 @@
 
             // Schedule this only after we invoke onClientFinished for the previous client, so that
             // internal preemption logic is not run.
-            mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
-                    operationId, user, cookie, listener, opPackageName, restricted, statsClient,
+            mFingerprint21.scheduleAuthenticate(token,
+                    operationId, cookie, listener, options, restricted, statsClient,
                     isKeyguard);
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 957005a..d22aef8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -26,6 +26,7 @@
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlay;
@@ -57,7 +58,8 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint>
+class FingerprintAuthenticationClient
+        extends AuthenticationClient<IBiometricsFingerprint, FingerprintAuthenticateOptions>
         implements Udfps {
 
     private static final String TAG = "Biometrics/FingerprintAuthClient";
@@ -72,9 +74,9 @@
     FingerprintAuthenticationClient(@NonNull Context context,
             @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
-            boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
-            int sensorId, @NonNull BiometricLogger logger,
+            @NonNull ClientMonitorCallbackConverter listener, long operationId,
+            boolean restricted, @NonNull FingerprintAuthenticateOptions options,
+            int cookie, boolean requireConfirmation, @NonNull BiometricLogger logger,
             @NonNull BiometricContext biometricContext, boolean isStrongBiometric,
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutFrameworkImpl lockoutTracker,
@@ -84,8 +86,8 @@
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @Authenticators.Types int sensorStrength) {
-        super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
-                owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
+        super(context, lazyDaemon, token, listener, operationId, restricted,
+                options, cookie, requireConfirmation, logger, biometricContext,
                 isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
                 false /* shouldVibrate */, sensorStrength);
         setRequestId(requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index cfa9fb4..362c820 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -24,6 +24,7 @@
 import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.IUdfpsOverlay;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
@@ -61,12 +62,13 @@
     public FingerprintDetectClient(@NonNull Context context,
             @NonNull Supplier<IBiometricsFingerprint> lazyDaemon,
             @NonNull IBinder token, long requestId,
-            @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
-            int sensorId,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull FingerprintAuthenticateOptions options,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
             @Nullable IUdfpsOverlay udfpsOverlay, boolean isStrongBiometric) {
-        super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+        super(context, lazyDaemon, token, listener, options.getUserId(),
+                options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
                 true /* shouldVibrate */, biometricLogger, biometricContext);
         setRequestId(requestId);
         mSensorOverlays = new SensorOverlays(udfpsOverlayController,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ab2c002..1ce917c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -3254,6 +3254,8 @@
             }
 
             mActiveNetwork = network;
+            mUnderlyingLinkProperties = null;
+            mUnderlyingNetworkCapabilities = null;
             mRetryCount = 0;
 
             startOrMigrateIkeSession(network);
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorService.java b/services/core/java/com/android/server/cpu/CpuMonitorService.java
index b0dfb84..4eefe5c 100644
--- a/services/core/java/com/android/server/cpu/CpuMonitorService.java
+++ b/services/core/java/com/android/server/cpu/CpuMonitorService.java
@@ -38,7 +38,7 @@
 /** 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);
+    static final boolean DEBUG = Log.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
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index 8e34601..fcaa957 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -4,5 +4,7 @@
 ogunwale@google.com
 santoscordon@google.com
 flc@google.com
+wilczynskip@google.com
+brup@google.com
 
 per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index da65f27..2efb0be 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -24,13 +24,14 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.util.Log;
 import android.view.inputmethod.ImeTracker;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IImeTracker;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.view.IImeTracker;
 
 import java.io.PrintWriter;
 import java.time.Instant;
@@ -53,7 +54,7 @@
 @SuppressWarnings("GuardedBy")
 public final class ImeTrackerService extends IImeTracker.Stub {
 
-    static final String TAG = "ImeTrackerService";
+    private static final String TAG = ImeTracker.TAG;
 
     /** The threshold in milliseconds after which a history entry is considered timed out. */
     private static final long TIMEOUT_MS = 10_000;
@@ -71,67 +72,71 @@
 
     @NonNull
     @Override
-    public synchronized IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
-            @SoftInputShowHideReason int reason) {
-        final IBinder binder = new Binder();
-        final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_SHOW,
-                ImeTracker.STATUS_RUN, origin, reason);
+    public synchronized ImeTracker.Token onRequestShow(@NonNull String tag, int uid,
+            @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+        final var binder = new Binder();
+        final var token = new ImeTracker.Token(binder, tag);
+        final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_SHOW, ImeTracker.STATUS_RUN,
+                origin, reason);
         mHistory.addEntry(binder, entry);
 
         // Register a delayed task to handle the case where the new entry times out.
         mHandler.postDelayed(() -> {
             synchronized (ImeTrackerService.this) {
-                mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
+                mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
             }
         }, TIMEOUT_MS);
 
-        return binder;
+        return token;
     }
 
     @NonNull
     @Override
-    public synchronized IBinder onRequestHide(int uid, @ImeTracker.Origin int origin,
-            @SoftInputShowHideReason int reason) {
-        final IBinder binder = new Binder();
-        final History.Entry entry = new History.Entry(uid, ImeTracker.TYPE_HIDE,
-                ImeTracker.STATUS_RUN, origin, reason);
+    public synchronized ImeTracker.Token onRequestHide(@NonNull String tag, int uid,
+            @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+        final var binder = new Binder();
+        final var token = new ImeTracker.Token(binder, tag);
+        final var entry = new History.Entry(tag, uid, ImeTracker.TYPE_HIDE, ImeTracker.STATUS_RUN,
+                origin, reason);
         mHistory.addEntry(binder, entry);
 
         // Register a delayed task to handle the case where the new entry times out.
         mHandler.postDelayed(() -> {
             synchronized (ImeTrackerService.this) {
-                mHistory.setFinished(binder, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
+                mHistory.setFinished(token, ImeTracker.STATUS_TIMEOUT, ImeTracker.PHASE_NOT_SET);
             }
         }, TIMEOUT_MS);
 
-        return binder;
+        return token;
     }
 
     @Override
-    public synchronized void onProgress(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
-        final History.Entry entry = mHistory.getEntry(statsToken);
+    public synchronized void onProgress(@NonNull IBinder binder, @ImeTracker.Phase int phase) {
+        final var entry = mHistory.getEntry(binder);
         if (entry == null) return;
 
         entry.mPhase = phase;
     }
 
     @Override
-    public synchronized void onFailed(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+    public synchronized void onFailed(@NonNull ImeTracker.Token statsToken,
+            @ImeTracker.Phase int phase) {
         mHistory.setFinished(statsToken, ImeTracker.STATUS_FAIL, phase);
     }
 
     @Override
-    public synchronized void onCancelled(@NonNull IBinder statsToken, @ImeTracker.Phase int phase) {
+    public synchronized void onCancelled(@NonNull ImeTracker.Token statsToken,
+            @ImeTracker.Phase int phase) {
         mHistory.setFinished(statsToken, ImeTracker.STATUS_CANCEL, phase);
     }
 
     @Override
-    public synchronized void onShown(@NonNull IBinder statsToken) {
+    public synchronized void onShown(@NonNull ImeTracker.Token statsToken) {
         mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
     }
 
     @Override
-    public synchronized void onHidden(@NonNull IBinder statsToken) {
+    public synchronized void onHidden(@NonNull ImeTracker.Token statsToken) {
         mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
     }
 
@@ -141,9 +146,9 @@
      * @param statsToken the token corresponding to the current IME request.
      * @param requestWindowName the name of the window that created the IME request.
      */
-    public synchronized void onImmsUpdate(@NonNull IBinder statsToken,
+    public synchronized void onImmsUpdate(@NonNull ImeTracker.Token statsToken,
             @NonNull String requestWindowName) {
-        final History.Entry entry = mHistory.getEntry(statsToken);
+        final var entry = mHistory.getEntry(statsToken.getBinder());
         if (entry == null) return;
 
         entry.mRequestWindowName = requestWindowName;
@@ -181,17 +186,17 @@
         /** Latest entry sequence number. */
         private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
 
-        /** Adds a live entry. */
+        /** Adds a live entry corresponding to the given IME tracking token's binder. */
         @GuardedBy("ImeTrackerService.this")
-        private void addEntry(@NonNull IBinder statsToken, @NonNull Entry entry) {
-            mLiveEntries.put(statsToken, entry);
+        private void addEntry(@NonNull IBinder binder, @NonNull Entry entry) {
+            mLiveEntries.put(binder, entry);
         }
 
-        /** Gets the entry corresponding to the given IME tracking token, if it exists. */
+        /** Gets the entry corresponding to the given IME tracking token's binder, if it exists. */
         @Nullable
         @GuardedBy("ImeTrackerService.this")
-        private Entry getEntry(@NonNull IBinder statsToken) {
-            return mLiveEntries.get(statsToken);
+        private Entry getEntry(@NonNull IBinder binder) {
+            return mLiveEntries.get(binder);
         }
 
         /**
@@ -204,10 +209,21 @@
          *              (or {@link ImeTracker#PHASE_NOT_SET} otherwise).
          */
         @GuardedBy("ImeTrackerService.this")
-        private void setFinished(@NonNull IBinder statsToken, @ImeTracker.Status int status,
-                @ImeTracker.Phase int phase) {
-            final Entry entry = mLiveEntries.remove(statsToken);
-            if (entry == null) return;
+        private void setFinished(@NonNull ImeTracker.Token statsToken,
+                @ImeTracker.Status int status, @ImeTracker.Phase int phase) {
+            final var entry = mLiveEntries.remove(statsToken.getBinder());
+            if (entry == null) {
+                // This will be unconditionally called through the postDelayed above to handle
+                // potential timeouts, and is thus intentionally dropped to avoid having to manually
+                // save and remove the registered callback. Only timeout calls are expected.
+                if (status != ImeTracker.STATUS_TIMEOUT) {
+                    Log.i(TAG, statsToken.getTag()
+                            + ": setFinished on previously finished token at "
+                            + ImeTracker.Debug.phaseToString(phase) + " with "
+                            + ImeTracker.Debug.statusToString(status));
+                }
+                return;
+            }
 
             entry.mDuration = System.currentTimeMillis() - entry.mStartTime;
             entry.mStatus = status;
@@ -216,6 +232,13 @@
                 entry.mPhase = phase;
             }
 
+            if (status == ImeTracker.STATUS_TIMEOUT) {
+                // All events other than timeouts are already logged in the client-side ImeTracker.
+                Log.i(TAG, statsToken.getTag() + ": setFinished at "
+                        + ImeTracker.Debug.phaseToString(entry.mPhase) + " with "
+                        + ImeTracker.Debug.statusToString(status));
+            }
+
             // Remove excess entries overflowing capacity (plus one for the new entry).
             while (mEntries.size() >= CAPACITY) {
                 mEntries.remove();
@@ -232,21 +255,22 @@
         /** Dumps the contents of the circular buffer. */
         @GuardedBy("ImeTrackerService.this")
         private void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
-            final DateTimeFormatter formatter =
-                    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
-                            .withZone(ZoneId.systemDefault());
+            final var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
+                    .withZone(ZoneId.systemDefault());
 
             pw.print(prefix);
-            pw.println("ImeTrackerService#History.mLiveEntries:");
+            pw.println("ImeTrackerService#History.mLiveEntries: "
+                    + mLiveEntries.size() + " elements");
 
-            for (final Entry entry: mLiveEntries.values()) {
+            for (final var entry: mLiveEntries.values()) {
                 dumpEntry(entry, pw, prefix, formatter);
             }
 
             pw.print(prefix);
-            pw.println("ImeTrackerService#History.mEntries:");
+            pw.println("ImeTrackerService#History.mEntries: "
+                    + mEntries.size() + " elements");
 
-            for (final Entry entry: mEntries) {
+            for (final var entry: mEntries) {
                 dumpEntry(entry, pw, prefix, formatter);
             }
         }
@@ -255,34 +279,22 @@
         private void dumpEntry(@NonNull Entry entry, @NonNull PrintWriter pw,
                 @NonNull String prefix, @NonNull DateTimeFormatter formatter) {
             pw.print(prefix);
-            pw.println("ImeTrackerService#History #" + entry.mSequenceNumber + ":");
+            pw.print(" #" + entry.mSequenceNumber);
+            pw.print(" " + ImeTracker.Debug.typeToString(entry.mType));
+            pw.print(" - " + ImeTracker.Debug.statusToString(entry.mStatus));
+            pw.print(" - " + entry.mTag);
+            pw.println(" (" + entry.mDuration + "ms):");
 
             pw.print(prefix);
-            pw.println(" startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime)));
+            pw.print("   startTime=" + formatter.format(Instant.ofEpochMilli(entry.mStartTime)));
+            pw.println(" " + ImeTracker.Debug.originToString(entry.mOrigin));
 
             pw.print(prefix);
-            pw.println(" duration=" + entry.mDuration + "ms");
+            pw.print("   reason=" + InputMethodDebug.softInputDisplayReasonToString(entry.mReason));
+            pw.println(" " + ImeTracker.Debug.phaseToString(entry.mPhase));
 
             pw.print(prefix);
-            pw.print(" type=" + ImeTracker.Debug.typeToString(entry.mType));
-
-            pw.print(prefix);
-            pw.print(" status=" + ImeTracker.Debug.statusToString(entry.mStatus));
-
-            pw.print(prefix);
-            pw.print(" origin="
-                    + ImeTracker.Debug.originToString(entry.mOrigin));
-
-            pw.print(prefix);
-            pw.print(" reason="
-                    + InputMethodDebug.softInputDisplayReasonToString(entry.mReason));
-
-            pw.print(prefix);
-            pw.print(" phase="
-                    + ImeTracker.Debug.phaseToString(entry.mPhase));
-
-            pw.print(prefix);
-            pw.print(" requestWindowName=" + entry.mRequestWindowName);
+            pw.println("   requestWindowName=" + entry.mRequestWindowName);
         }
 
         /** A history entry. */
@@ -291,6 +303,10 @@
             /** The entry's sequence number in the history. */
             private final int mSequenceNumber = sSequenceNumber.getAndIncrement();
 
+            /** Logging tag, of the shape "component:random_hexadecimal". */
+            @NonNull
+            private final String mTag;
+
             /** Uid of the client that requested the IME. */
             private final int mUid;
 
@@ -323,13 +339,15 @@
             /**
              * Name of the window that created the IME request.
              *
-             * Note: This is later set through {@link #onImmsUpdate(IBinder, String)}.
+             * Note: This is later set through {@link #onImmsUpdate}.
              */
             @NonNull
             private String mRequestWindowName = "not set";
 
-            private Entry(int uid, @ImeTracker.Type int type, @ImeTracker.Status int status,
-                    @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+            private Entry(@NonNull String tag, int uid, @ImeTracker.Type int type,
+                    @ImeTracker.Status int status, @ImeTracker.Origin int origin,
+                    @SoftInputShowHideReason int reason) {
+                mTag = tag;
                 mUid = uid;
                 mType = type;
                 mStatus = status;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8ef4e4a..f142293 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -145,6 +145,7 @@
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.DirectBootAwareness;
 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IImeTracker;
 import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethod;
@@ -168,7 +169,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.view.IImeTracker;
 import com.android.internal.view.IInputMethodManager;
 import com.android.server.AccessibilityManagerInternal;
 import com.android.server.EventLogTags;
@@ -532,6 +532,7 @@
     /**
      * The client that is currently bound to an input method.
      */
+    @Nullable
     private ClientState mCurClient;
 
     /**
@@ -557,11 +558,26 @@
     int mCurFocusedWindowSoftInputMode;
 
     /**
-     * The client by which {@link #mCurFocusedWindow} was reported.
+     * The client by which {@link #mCurFocusedWindow} was reported. This gets updated whenever an
+     * IME-focusable window gained focus (without necessarily starting an input connection),
+     * while {@link #mCurClient} only gets updated when we actually start an input connection.
+     *
+     * @see #mCurFocusedWindow
      */
+    @Nullable
     ClientState mCurFocusedWindowClient;
 
     /**
+     * The editor info by which {@link #mCurFocusedWindow} was reported. This differs from
+     * {@link #mCurEditorInfo} the same way {@link #mCurFocusedWindowClient} differs
+     * from {@link #mCurClient}.
+     *
+     * @see #mCurFocusedWindow
+     */
+    @Nullable
+    EditorInfo mCurFocusedWindowEditorInfo;
+
+    /**
      * The {@link IRemoteInputConnection} last provided by the current client.
      */
     IRemoteInputConnection mCurInputConnection;
@@ -580,6 +596,7 @@
     /**
      * The {@link EditorInfo} last provided by the current client.
      */
+    @Nullable
     EditorInfo mCurEditorInfo;
 
     /**
@@ -2265,6 +2282,7 @@
                 }
                 if (mCurFocusedWindowClient == cs) {
                     mCurFocusedWindowClient = null;
+                    mCurFocusedWindowEditorInfo = null;
                 }
             }
         }
@@ -3453,10 +3471,7 @@
             ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
         // Create statsToken is none exists.
         if (statsToken == null) {
-            // TODO(b/261565259): to avoid using null, add package name in ClientState
-            final String packageName = (mCurEditorInfo != null) ? mCurEditorInfo.packageName : null;
-            final int uid = mCurClient != null ? mCurClient.mUid : -1;
-            statsToken = ImeTracker.forLogging().onRequestShow(packageName, uid,
+            statsToken = createStatsTokenForFocusedClient(true /* show */,
                     ImeTracker.ORIGIN_SERVER_START_INPUT, reason);
         }
 
@@ -3530,17 +3545,7 @@
             int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
         // Create statsToken is none exists.
         if (statsToken == null) {
-            // TODO(b/261565259): to avoid using null, add package name in ClientState
-            final String packageName = (mCurEditorInfo != null) ? mCurEditorInfo.packageName : null;
-            final int uid;
-            if (mCurClient != null) {
-                uid = mCurClient.mUid;
-            } else if (mCurFocusedWindowClient != null) {
-                uid = mCurFocusedWindowClient.mUid;
-            } else {
-                uid = -1;
-            }
-            statsToken = ImeTracker.forLogging().onRequestHide(packageName, uid,
+            statsToken = createStatsTokenForFocusedClient(false /* show */,
                     ImeTracker.ORIGIN_SERVER_HIDE_INPUT, reason);
         }
 
@@ -3775,6 +3780,7 @@
         mCurFocusedWindow = windowToken;
         mCurFocusedWindowSoftInputMode = softInputMode;
         mCurFocusedWindowClient = cs;
+        mCurFocusedWindowEditorInfo = editorInfo;
         mCurPerceptible = true;
 
         // We want to start input before showing the IME, but after closing
@@ -4703,13 +4709,13 @@
                 mWindowManagerInternal.onToggleImeRequested(
                         show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
         mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
-                mCurFocusedWindowClient, mCurEditorInfo, info.focusedWindowName,
+                mCurFocusedWindowClient, mCurFocusedWindowEditorInfo, info.focusedWindowName,
                 mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
                 info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
                 info.imeSurfaceParentName));
 
         if (statsToken != null) {
-            mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
+            mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName);
         }
     }
 
@@ -5751,9 +5757,11 @@
             // We cannot simply distinguish a bad IME that reports an arbitrary package name from
             // an unfortunate IME whose internal state is already obsolete due to the asynchronous
             // nature of our system.  Let's compare it with our internal record.
-            if (!TextUtils.equals(mCurEditorInfo.packageName, packageName)) {
+            final var curPackageName = mCurEditorInfo != null
+                    ? mCurEditorInfo.packageName : null;
+            if (!TextUtils.equals(curPackageName, packageName)) {
                 Slog.e(TAG, "Ignoring createInputContentUriToken mCurEditorInfo.packageName="
-                        + mCurEditorInfo.packageName + " packageName=" + packageName);
+                        + curPackageName + " packageName=" + packageName);
                 return null;
             }
             // This user ID can never bee spoofed.
@@ -6514,6 +6522,30 @@
         return mImeTrackerService;
     }
 
+    /**
+     * Creates an IME request tracking token for the current focused client.
+     *
+     * @param show whether this is a show or a hide request.
+     * @param origin the origin of the IME request.
+     * @param reason the reason why the IME request was created.
+     */
+    @NonNull
+    private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
+            @ImeTracker.Origin int origin, @SoftInputShowHideReason int reason) {
+        final int uid = mCurFocusedWindowClient != null
+                ? mCurFocusedWindowClient.mUid
+                : -1;
+        final var packageName = mCurFocusedWindowEditorInfo != null
+                ? mCurFocusedWindowEditorInfo.packageName
+                : "uid(" + uid + ")";
+
+        if (show) {
+            return ImeTracker.forLogging().onRequestShow(packageName, uid, origin, reason);
+        } else {
+            return ImeTracker.forLogging().onRequestHide(packageName, uid, origin, reason);
+        }
+    }
+
     private static final class InputMethodPrivilegedOperationsImpl
             extends IInputMethodPrivilegedOperations.Stub {
         private final InputMethodManagerService mImms;
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 8f65775..94f12dd 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -370,11 +370,6 @@
             mLastRestartTimestampMap.put(contextHubId,
                     new AtomicLong(SystemClock.elapsedRealtimeNanos()));
 
-            IContextHubClient client = mClientManager.registerClient(
-                    contextHubInfo, createDefaultClientCallback(contextHubId),
-                    /* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
-            defaultClientMap.put(contextHubId, client);
-
             try {
                 mContextHubWrapper.registerCallback(contextHubId,
                         new ContextHubServiceCallback(contextHubId));
@@ -383,6 +378,11 @@
                         + contextHubId + ")", e);
             }
 
+            IContextHubClient client = mClientManager.registerClient(
+                    contextHubInfo, createDefaultClientCallback(contextHubId),
+                    /* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
+            defaultClientMap.put(contextHubId, client);
+
             // Do a query to initialize the service cache list of nanoapps
             // TODO(b/194289715): Remove this when old API is deprecated
             queryNanoAppsInternal(contextHubId);
@@ -1207,7 +1207,7 @@
         pw.println("");
         pw.println("=================== NANOAPPS ====================");
         // Dump nanoAppHash
-        mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
+        mNanoAppStateManager.foreachNanoAppInstanceInfo(pw::println);
 
         pw.println("");
         pw.println("=================== PRELOADED NANOAPPS ====================");
@@ -1255,16 +1255,17 @@
         proto.flush();
     }
 
-    /**
-     * Dumps preloaded nanoapps to the console
-     */
+    /** Dumps preloaded nanoapps to the console */
     private void dumpPreloadedNanoapps(PrintWriter pw) {
         if (mContextHubWrapper == null) {
             return;
         }
 
         long[] preloadedNanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
-        for (long preloadedNanoappId: preloadedNanoappIds) {
+        if (preloadedNanoappIds == null) {
+            return;
+        }
+        for (long preloadedNanoappId : preloadedNanoappIds) {
             pw.print("ID: 0x");
             pw.println(Long.toHexString(preloadedNanoappId));
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 93b0dcb..41592bd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4410,10 +4410,9 @@
             pw.println("            " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
         }
         pw.println("      --reset: restore package to its post-install state");
-        pw.println("      --check-prof (true | false): look at profiles when doing dexopt?");
+        pw.println("      --check-prof (true | false): ignored - this is always true");
         pw.println("      --secondary-dex: compile app secondary dex files");
         pw.println("      --split SPLIT: compile only the given split name");
-        pw.println("      --compile-layouts: compile layout resources for faster inflation");
         pw.println("");
         pw.println("  force-dex-opt PACKAGE");
         pw.println("    Force immediate execution of dex opt for the given PACKAGE.");
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index db44e14..0e99e7e 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -194,27 +194,38 @@
 
         mPackageManagerInternal.getPackageList(new PackageListObserver() {
             @Override
-            public void onPackageAdded(String packageName, int uid) {
-                final int userId = UserHandle.getUserId(uid);
-                if (isStarted(userId)) {
-                    synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
+            public void onPackageAdded(String packageName, int appId) {
+                final int[] userIds = LocalServices.getService(UserManagerInternal.class)
+                        .getUserIds();
+                for (final int userId : userIds) {
+                    if (isStarted(userId)) {
+                        synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
+                    }
                 }
             }
 
             @Override
-            public void onPackageChanged(String packageName, int uid) {
-                final int userId = UserHandle.getUserId(uid);
-                if (isStarted(userId)) {
-                    synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
-                    resetAppOpPermissionsIfNotRequestedForUid(uid);
+            public void onPackageChanged(String packageName, int appId) {
+                final int[] userIds = LocalServices.getService(UserManagerInternal.class)
+                        .getUserIds();
+                for (final int userId : userIds) {
+                    if (isStarted(userId)) {
+                        synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
+                        final int uid = UserHandle.getUid(userId, appId);
+                        resetAppOpPermissionsIfNotRequestedForUid(uid);
+                    }
                 }
             }
 
             @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                final int userId = UserHandle.getUserId(uid);
-                if (isStarted(userId)) {
-                    resetAppOpPermissionsIfNotRequestedForUid(uid);
+            public void onPackageRemoved(String packageName, int appId) {
+                final int[] userIds = LocalServices.getService(UserManagerInternal.class)
+                        .getUserIds();
+                for (final int userId : userIds) {
+                    if (isStarted(userId)) {
+                        final int uid = UserHandle.getUid(userId, appId);
+                        resetAppOpPermissionsIfNotRequestedForUid(uid);
+                    }
                 }
             }
         });
diff --git a/services/core/java/com/android/server/rollback/OWNERS b/services/core/java/com/android/server/rollback/OWNERS
index 7feb85f..daa0211 100644
--- a/services/core/java/com/android/server/rollback/OWNERS
+++ b/services/core/java/com/android/server/rollback/OWNERS
@@ -1 +1,3 @@
-olilan@google.com
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
index ed3248ec..de631bb 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
@@ -181,24 +181,6 @@
     }
 
     /**
-     * Clears the cached NTP time. For use during tests to simulate when no NTP time is available.
-     *
-     * <p>This operation takes place in the calling thread rather than the service's handler thread.
-     */
-    @RequiresPermission(android.Manifest.permission.SET_TIME)
-    void clearTimeForTests() {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.SET_TIME, "clear latest network time");
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNtpTrustedTime.clearCachedTimeResult();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
      * Forces the service to refresh the NTP time.
      *
      * <p>This operation takes place in the calling thread rather than the service's handler thread.
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
index afc0bdd..cfc95df 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
@@ -37,11 +37,6 @@
     private static final String SHELL_COMMAND_SERVICE_NAME = "network_time_update_service";
 
     /**
-     * A shell command that clears the time signal received from the network.
-     */
-    private static final String SHELL_COMMAND_CLEAR_TIME = "clear_time";
-
-    /**
      * A shell command that forces the time signal to be refreshed from the network.
      */
     private static final String SHELL_COMMAND_FORCE_REFRESH = "force_refresh";
@@ -73,8 +68,6 @@
         }
 
         switch (cmd) {
-            case SHELL_COMMAND_CLEAR_TIME:
-                return runClearTime();
             case SHELL_COMMAND_FORCE_REFRESH:
                 return runForceRefresh();
             case SHELL_COMMAND_SET_SERVER_CONFIG:
@@ -87,11 +80,6 @@
         }
     }
 
-    private int runClearTime() {
-        mNetworkTimeUpdateService.clearTimeForTests();
-        return 0;
-    }
-
     private int runForceRefresh() {
         boolean success = mNetworkTimeUpdateService.forceRefreshForTests();
         getOutPrintWriter().println(success);
@@ -147,8 +135,6 @@
         pw.printf("Network Time Update Service (%s) commands:\n", SHELL_COMMAND_SERVICE_NAME);
         pw.printf("  help\n");
         pw.printf("    Print this help text.\n");
-        pw.printf("  %s\n", SHELL_COMMAND_CLEAR_TIME);
-        pw.printf("    Clears the latest time.\n");
         pw.printf("  %s\n", SHELL_COMMAND_FORCE_REFRESH);
         pw.printf("    Refreshes the latest time. Prints whether it was successful.\n");
         pw.printf("  %s\n", SHELL_COMMAND_SET_SERVER_CONFIG);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0da967a..22f096b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -37,6 +37,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.NtpTrustedTime;
@@ -53,6 +54,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.net.InetSocketAddress;
 import java.time.DateTimeException;
 import java.util.Objects;
 
@@ -377,7 +379,7 @@
      *
      * <p>This operation takes place in the calling thread.
      */
-    void clearNetworkTime() {
+    void clearLatestNetworkTime() {
         enforceSuggestNetworkTimePermission();
 
         final long token = Binder.clearCallingIdentity();
@@ -390,12 +392,29 @@
 
     @Override
     public UnixEpochTime latestNetworkTime() {
-        NetworkTimeSuggestion suggestion = getLatestNetworkSuggestion();
-        if (suggestion != null) {
-            return suggestion.getUnixEpochTime();
+        NetworkTimeSuggestion latestNetworkTime;
+        // TODO(b/222295093): Remove this condition once we can be sure that all uses of
+        //  NtpTrustedTime result in a suggestion being made to the time detector.
+        //  mNtpTrustedTime can be removed once this happens.
+        if (TimeDetectorNetworkTimeHelper.isInUse()) {
+            // The new implementation.
+            latestNetworkTime = mTimeDetectorStrategy.getLatestNetworkSuggestion();
         } else {
+            // The old implementation.
+            NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
+            if (ntpResult != null) {
+                latestNetworkTime = new NetworkTimeSuggestion(
+                        new UnixEpochTime(
+                                ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis()),
+                        ntpResult.getUncertaintyMillis());
+            } else {
+                latestNetworkTime = null;
+            }
+        }
+        if (latestNetworkTime == null) {
             throw new ParcelableException(new DateTimeException("Missing network time fix"));
         }
+        return latestNetworkTime.getUnixEpochTime();
     }
 
     /**
@@ -403,23 +422,7 @@
      */
     @Nullable
     NetworkTimeSuggestion getLatestNetworkSuggestion() {
-        // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
-        //  be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
-        //  detector. mNtpTrustedTime can be removed once this happens.
-        if (TimeDetectorNetworkTimeHelper.isInUse()) {
-            // The new implementation.
-            return mTimeDetectorStrategy.getLatestNetworkSuggestion();
-        } else {
-            // The old implementation.
-            NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
-            if (ntpResult != null) {
-                UnixEpochTime unixEpochTime = new UnixEpochTime(
-                        ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
-                return new NetworkTimeSuggestion(unixEpochTime, ntpResult.getUncertaintyMillis());
-            } else {
-                return null;
-            }
-        }
+        return mTimeDetectorStrategy.getLatestNetworkSuggestion();
     }
 
     /**
@@ -440,6 +443,57 @@
         mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal));
     }
 
+    /**
+     * Sets the network time for testing {@link SystemClock#currentNetworkTimeClock()}.
+     *
+     * <p>This operation takes place in the calling thread.
+     */
+    void setNetworkTimeForSystemClockForTests(
+            @NonNull UnixEpochTime unixEpochTime, int uncertaintyMillis) {
+        enforceSuggestNetworkTimePermission();
+
+        // TODO(b/222295093): Remove this condition once we can be sure that all uses of
+        //  NtpTrustedTime result in a suggestion being made to the time detector.
+        //  mNtpTrustedTime can be removed once this happens.
+        if (TimeDetectorNetworkTimeHelper.isInUse()) {
+            NetworkTimeSuggestion suggestion =
+                    new NetworkTimeSuggestion(unixEpochTime, uncertaintyMillis);
+            suggestion.addDebugInfo("Injected for tests");
+            mTimeDetectorStrategy.suggestNetworkTime(suggestion);
+        } else {
+            NtpTrustedTime.TimeResult timeResult = new NtpTrustedTime.TimeResult(
+                    unixEpochTime.getUnixEpochTimeMillis(),
+                    unixEpochTime.getElapsedRealtimeMillis(),
+                    uncertaintyMillis,
+                    InetSocketAddress.createUnresolved("time.set.for.tests", 123));
+            mNtpTrustedTime.setCachedTimeResult(timeResult);
+        }
+    }
+
+    /**
+     * Clears the network time for testing {@link SystemClock#currentNetworkTimeClock()}.
+     *
+     * <p>This operation takes place in the calling thread.
+     */
+    void clearNetworkTimeForSystemClockForTests() {
+        enforceSuggestNetworkTimePermission();
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // TODO(b/222295093): Remove this condition once we can be sure that all uses of
+            //  NtpTrustedTime result in a suggestion being made to the time detector.
+            //  mNtpTrustedTime can be removed once this happens.
+            if (TimeDetectorNetworkTimeHelper.isInUse()) {
+                // Clear the latest network suggestion. Done in all c
+                mTimeDetectorStrategy.clearLatestNetworkSuggestion();
+            } else {
+                mNtpTrustedTime.clearCachedTimeResult();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @Override
     protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
             @Nullable String[] args) {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
index cce5709..fe0127f 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
@@ -16,12 +16,14 @@
 package com.android.server.timedetector;
 
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CLEAR_NETWORK_TIME;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CONFIRM_TIME;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_NETWORK_TIME;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_TIME_STATE;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SERVICE_NAME;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_TIME_STATE;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_EXTERNAL_TIME;
 import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_GNSS_TIME;
@@ -73,9 +75,9 @@
             case SHELL_COMMAND_SUGGEST_NETWORK_TIME:
                 return runSuggestNetworkTime();
             case SHELL_COMMAND_GET_NETWORK_TIME:
-                return runGetNetworkTime();
+                return runGetLatestNetworkTime();
             case SHELL_COMMAND_CLEAR_NETWORK_TIME:
-                return runClearNetworkTime();
+                return runClearLatestNetworkTime();
             case SHELL_COMMAND_SUGGEST_GNSS_TIME:
                 return runSuggestGnssTime();
             case SHELL_COMMAND_SUGGEST_EXTERNAL_TIME:
@@ -86,6 +88,10 @@
                 return runSetTimeState();
             case SHELL_COMMAND_CONFIRM_TIME:
                 return runConfirmTime();
+            case SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME:
+                return runClearSystemClockNetworkTime();
+            case SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME:
+                return runSetSystemClockNetworkTime();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -128,15 +134,15 @@
                 mInterface::suggestNetworkTime);
     }
 
-    private int runGetNetworkTime() {
+    private int runGetLatestNetworkTime() {
         NetworkTimeSuggestion networkTimeSuggestion = mInterface.getLatestNetworkSuggestion();
         final PrintWriter pw = getOutPrintWriter();
         pw.println(networkTimeSuggestion);
         return 0;
     }
 
-    private int runClearNetworkTime() {
-        mInterface.clearNetworkTime();
+    private int runClearLatestNetworkTime() {
+        mInterface.clearLatestNetworkTime();
         return 0;
     }
 
@@ -187,6 +193,20 @@
         return 0;
     }
 
+    private int runClearSystemClockNetworkTime() {
+        mInterface.clearNetworkTimeForSystemClockForTests();
+        return 0;
+    }
+
+    private int runSetSystemClockNetworkTime() {
+        NetworkTimeSuggestion networkTimeSuggestion =
+                NetworkTimeSuggestion.parseCommandLineArg(this);
+        mInterface.setNetworkTimeForSystemClockForTests(
+                networkTimeSuggestion.getUnixEpochTime(),
+                networkTimeSuggestion.getUncertaintyMillis());
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -218,6 +238,16 @@
         pw.printf("    Prints the network time information held by the detector.\n");
         pw.printf("  %s\n", SHELL_COMMAND_CLEAR_NETWORK_TIME);
         pw.printf("    Clears the network time information held by the detector.\n");
+        // TODO(b/222295093) Remove these "system_clock" commands when
+        //  SystemClock.currentNetworkTimeClock() is guaranteed to use the latest network
+        //  suggestion. Then, commands above can be used instead.
+        pw.printf("  %s <network suggestion opts>\n",
+                SHELL_COMMAND_SET_SYSTEM_CLOCK_NETWORK_TIME);
+        pw.printf("    Sets the network time information used for"
+                + " SystemClock.currentNetworkTimeClock().\n");
+        pw.printf("  %s\n", SHELL_COMMAND_CLEAR_SYSTEM_CLOCK_NETWORK_TIME);
+        pw.printf("    Clears the network time information used for"
+                + " SystemClock.currentNetworkTimeClock().\n");
         pw.println();
         ManualTimeSuggestion.printCommandLineOpts(pw);
         pw.println();
diff --git a/services/core/java/com/android/server/utils/Slogf.java b/services/core/java/com/android/server/utils/Slogf.java
index 6efbd89..a4b2bfb 100644
--- a/services/core/java/com/android/server/utils/Slogf.java
+++ b/services/core/java/com/android/server/utils/Slogf.java
@@ -30,11 +30,13 @@
 /**
  * Extends {@link Slog} by providing overloaded methods that take string formatting.
  *
- * <p><strong>Note: </strong>the overloaded methods won't create the formatted message if the
- * respective logging level is disabled for the tag, but the compiler will still create an
- * intermediate array of the objects for the {@code vargars}, which could affect garbage collection.
- * So, if you're calling these method in a critical path, make sure to explicitly check for the
- * level before calling them.
+ * <p><strong>Note: </strong>Like the other logging classes, e.g. {@link Log} and {@link Slog}, the
+ * methods in this class log unconditionally regardless of {@link Log#isLoggable(String, int)}.
+ * Therefore, these methods exist just for the convenience of handling formatting.  (Even if they
+ * did check {@link Log#isLoggable(String, int)} before formatting and logging, calling a varargs
+ * method in Java still involves an array allocation.)  If you need to avoid the overhead of logging
+ * on a performance-critical path, either don't use logging in that place, or make the logging
+ * conditional on a static boolean defaulting to false.
  */
 public final class Slogf {
 
@@ -56,11 +58,6 @@
         throw new UnsupportedOperationException("provides only static methods");
     }
 
-    /** Same as {@link Log#isLoggable(String, int)}. */
-    public static boolean isLoggable(String tag, int level) {
-        return Log.isLoggable(tag, level);
-    }
-
     /** Same as {@link Slog#v(String, String)}. */
     public static int v(String tag, String msg) {
         return Slog.v(tag, msg);
@@ -146,166 +143,62 @@
         return Slog.println(priority, tag, msg);
     }
 
-    /**
-     * Logs a {@link Log.VERBOSE} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#VERBOSE} logging
-     * is enabled for the given {@code tag}, but the compiler will still create an intermediate
-     * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
-     * you're calling this method in a critical path, make sure to explicitly do the check before
-     * calling it.
-     */
+    /** Logs a {@link Log.VERBOSE} message. */
     public static void v(String tag, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.VERBOSE)) return;
-
         v(tag, getMessage(format, args));
     }
 
-    /**
-     * Logs a {@link Log.VEBOSE} message with a throwable
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#VERBOSE} logging
-     * is enabled for the given {@code tag}, but the compiler will still create an intermediate
-     * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
-     * you're calling this method in a critical path, make sure to explicitly do the check before
-     * calling it.
-     */
+    /** Logs a {@link Log.VERBOSE} message with a throwable. */
     public static void v(String tag, Throwable throwable, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.VERBOSE)) return;
-
         v(tag, getMessage(format, args), throwable);
     }
 
-    /**
-     * Logs a {@link Log.DEBUG} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#DEBUG} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     */
+    /** Logs a {@link Log.DEBUG} message. */
     public static void d(String tag, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.DEBUG)) return;
-
         d(tag, getMessage(format, args));
     }
 
-    /**
-     * Logs a {@link Log.DEBUG} message with a throwable
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#DEBUG} logging
-     * is enabled for the given {@code tag}, but the compiler will still create an intermediate
-     * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
-     * you're calling this method in a critical path, make sure to explicitly do the check before
-     * calling it.
-     */
+    /** Logs a {@link Log.DEBUG} message with a throwable. */
     public static void d(String tag, Throwable throwable, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.DEBUG)) return;
-
         d(tag, getMessage(format, args), throwable);
     }
 
-    /**
-     * Logs a {@link Log.INFO} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#INFO} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     */
+    /** Logs a {@link Log.INFO} message. */
     public static void i(String tag, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.INFO)) return;
-
         i(tag, getMessage(format, args));
     }
 
-    /**
-     * Logs a {@link Log.INFO} message with a throwable
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#INFO} logging
-     * is enabled for the given {@code tag}, but the compiler will still create an intermediate
-     * array of the objects for the {@code vargars}, which could affect garbage collection. So, if
-     * you're calling this method in a critical path, make sure to explicitly do the check before
-     * calling it.
-     */
+    /** Logs a {@link Log.INFO} message with a throwable. */
     public static void i(String tag, Throwable throwable, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.INFO)) return;
-
         i(tag, getMessage(format, args), throwable);
     }
 
-    /**
-     * Logs a {@link Log.WARN} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     */
+    /** Logs a {@link Log.WARN} message. */
     public static void w(String tag, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.WARN)) return;
-
         w(tag, getMessage(format, args));
     }
 
-    /**
-     * Logs a {@link Log.WARN} message with a throwable
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     */
+    /** Logs a {@link Log.WARN} message with a throwable. */
     public static void w(String tag, Throwable throwable, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.WARN)) return;
-
         w(tag, getMessage(format, args), throwable);
     }
 
-    /**
-     * Logs a {@link Log.ERROR} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#ERROR} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     */
+    /** Logs a {@link Log.ERROR} message. */
     public static void e(String tag, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.ERROR)) return;
-
         e(tag, getMessage(format, args));
     }
 
-    /**
-     * Logs a {@link Log.ERROR} message with a throwable
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#ERROR} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     */
+    /** Logs a {@link Log.ERROR} message with a throwable. */
     public static void e(String tag, Throwable throwable, String format, @Nullable Object... args) {
-        if (!isLoggable(tag, Log.ERROR)) return;
-
         e(tag, getMessage(format, args), throwable);
     }
 
-    /**
-     * Logs a {@code wtf} message.
-     */
+    /** Logs a {@code wtf} message. */
     public static void wtf(String tag, String format, @Nullable Object... args) {
         wtf(tag, getMessage(format, args));
     }
 
-    /**
-     * Logs a {@code wtf} message with a throwable.
-     */
+    /** Logs a {@code wtf} message with a throwable. */
     public static void wtf(String tag, Throwable throwable, String format,
             @Nullable Object... args) {
         wtf(tag, getMessage(format, args), throwable);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c41acd0..18be2a3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -65,6 +65,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -201,6 +202,7 @@
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.Process;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
@@ -227,7 +229,6 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationRunner;
-import android.view.IWindowFocusObserver;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
@@ -1852,11 +1853,11 @@
 
     @Override
     public BackNavigationInfo startBackNavigation(
-            IWindowFocusObserver observer, BackAnimationAdapter adapter) {
+            RemoteCallback navigationObserver, BackAnimationAdapter adapter) {
         mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
                 "startBackNavigation()");
 
-        return mBackNavigationController.startBackNavigation(observer, adapter);
+        return mBackNavigationController.startBackNavigation(navigationObserver, adapter);
     }
 
     /**
@@ -2034,7 +2035,20 @@
             return;
         }
 
-        if (r.moveFocusableActivityToTop("setFocusedTask")) {
+        final Transition transition = (getTransitionController().isCollecting()
+                || !getTransitionController().isShellTransitionsEnabled()) ? null
+                : getTransitionController().createTransition(TRANSIT_TO_FRONT);
+        if (transition != null) {
+            // Set ready before doing anything. If order does change, then that will set it unready
+            // so that we wait for the new lifecycles to complete.
+            transition.setReady(task, true /* ready */);
+        }
+        final boolean movedToTop = r.moveFocusableActivityToTop("setFocusedTask");
+        if (movedToTop) {
+            if (transition != null) {
+                getTransitionController().requestStartTransition(
+                        transition, null /* startTask */, null /* remote */, null /* display */);
+            }
             mRootWindowContainer.resumeFocusedTasksTopActivities();
         } else if (touchedActivity != null && touchedActivity.isFocusable()) {
             final TaskFragment parent = touchedActivity.getTaskFragment();
@@ -2046,6 +2060,10 @@
                         true /* updateInputWindows */);
             }
         }
+        if (transition != null && !movedToTop) {
+            // No order changes and focus-changes, alone, aren't captured in transitions.
+            transition.abort();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 75c85d7..4e94f96 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -253,7 +253,7 @@
 
         ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
         ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
-        if (mDisplayContent.mAtmService.mBackNavigationController.isWaitBackTransition()) {
+        if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringTransition()) {
             tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
             tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
             if (mDisplayContent.mAtmService.mBackNavigationController
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index bc5f67b..a229fc5 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -41,7 +41,6 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.IWindowFocusObserver;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.WindowInsets;
@@ -66,11 +65,11 @@
 class BackNavigationController {
     private static final String TAG = "BackNavigationController";
     private WindowManagerService mWindowManagerService;
-    private IWindowFocusObserver mFocusObserver;
     private boolean mBackAnimationInProgress;
     private @BackNavigationInfo.BackTargetType int mLastBackType;
     private boolean mShowWallpaper;
     private Runnable mPendingAnimation;
+    private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
 
     private AnimationHandler mAnimationHandler;
     private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
@@ -86,6 +85,11 @@
         return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0;
     }
 
+    // Notify focus window changed
+    void onFocusChanged(WindowState newFocus) {
+        mNavigationMonitor.onFocusWindowChanged(newFocus);
+    }
+
     /**
      * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
      * back gesture animation.
@@ -96,13 +100,12 @@
      */
     @VisibleForTesting
     @Nullable
-    BackNavigationInfo startBackNavigation(
-            IWindowFocusObserver observer, BackAnimationAdapter adapter) {
+    BackNavigationInfo startBackNavigation(@NonNull RemoteCallback navigationObserver,
+            BackAnimationAdapter adapter) {
         if (!sPredictBackEnable) {
             return null;
         }
         final WindowManagerService wmService = mWindowManagerService;
-        mFocusObserver = observer;
 
         int backType = BackNavigationInfo.TYPE_UNDEFINED;
 
@@ -202,9 +205,7 @@
                     backType = BackNavigationInfo.TYPE_CALLBACK;
                 }
                 infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
-                if (mFocusObserver != null) {
-                    window.registerFocusObserver(mFocusObserver);
-                }
+                mNavigationMonitor.startMonitor(window, navigationObserver);
             }
 
             ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -228,10 +229,8 @@
                     || currentActivity.isActivityTypeHome()
                     || currentActivity.mHasSceneTransition) {
                 infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK);
-                final WindowState finalFocusedWindow = window;
                 infoBuilder.setOnBackNavigationDone(new RemoteCallback(result ->
-                        onBackNavigationDone(result, finalFocusedWindow,
-                                BackNavigationInfo.TYPE_CALLBACK)));
+                        onBackNavigationDone(result, BackNavigationInfo.TYPE_CALLBACK)));
                 mLastBackType = BackNavigationInfo.TYPE_CALLBACK;
                 return infoBuilder.build();
             }
@@ -334,16 +333,19 @@
         WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
         if (finalRemovedWindowContainer != null) {
             final int finalBackType = backType;
-            final WindowState finalFocusedWindow = window;
             RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
-                    result, finalFocusedWindow, finalBackType));
+                    result, finalBackType));
             infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
         }
         mLastBackType = backType;
         return infoBuilder.build();
     }
 
-    boolean isWaitBackTransition() {
+    boolean isMonitoringTransition() {
+        return isWaitBackTransition() || mNavigationMonitor.isMonitoring();
+    }
+
+    private boolean isWaitBackTransition() {
         return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition;
     }
 
@@ -363,11 +365,23 @@
      */
     boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
             ArraySet<ActivityRecord> closeApps) {
-        if (!isWaitBackTransition()) {
+        if (!isMonitoringTransition()) {
             return false;
         }
         mTmpCloseApps.addAll(closeApps);
-        boolean result = false;
+        final boolean matchAnimationTargets = removeIfWaitForBackTransition(openApps, closeApps);
+        if (!matchAnimationTargets) {
+            mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
+        }
+        mTmpCloseApps.clear();
+        return matchAnimationTargets;
+    }
+
+    boolean removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps,
+            ArraySet<ActivityRecord> closeApps) {
+        if (!isWaitBackTransition()) {
+            return false;
+        }
         // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from
         // mOpeningApps if there is no visibility change.
         if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) {
@@ -386,10 +400,76 @@
                     closeApps.removeAt(i);
                 }
             }
-            result = true;
+            return true;
         }
-        mTmpCloseApps.clear();
-        return result;
+        return false;
+    }
+
+    private static class NavigationMonitor {
+        // The window which triggering the back navigation.
+        private WindowState mNavigatingWindow;
+        private RemoteCallback mObserver;
+
+        void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) {
+            mNavigatingWindow = window;
+            mObserver = observer;
+        }
+
+        void stopMonitor() {
+            mNavigatingWindow = null;
+            mObserver = null;
+        }
+
+        boolean isMonitoring() {
+            return mNavigatingWindow != null && mObserver != null;
+        }
+
+        /**
+         * Notify focus window changed during back navigation. This will cancel the gesture for
+         * scenarios like: a system window popup, or when an activity add a new window.
+         *
+         * This method should only be used to check window-level change, otherwise it may cause
+         * misjudgment in multi-window mode. For example: in split-screen, when user is
+         * navigating on the top task, bottom task can start a new task, which will gain focus for
+         * a short time, but we should not cancel the navigation.
+         */
+        private void onFocusWindowChanged(WindowState newFocus) {
+            if (!isMonitoring() || !atSameDisplay(newFocus)) {
+                return;
+            }
+            // Keep navigating if either new focus == navigating window or null.
+            if (newFocus != null && newFocus != mNavigatingWindow
+                    && (newFocus.mActivityRecord == null
+                    || (newFocus.mActivityRecord == mNavigatingWindow.mActivityRecord))) {
+                EventLogTags.writeWmBackNaviCanceled("focusWindowChanged");
+                mObserver.sendResult(null /* result */);
+            }
+        }
+
+        /**
+         * Notify an unexpected transition has happened during back navigation.
+         */
+        private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
+                ArrayList<WindowContainer> closing) {
+            if (!isMonitoring()) {
+                return;
+            }
+            final ArrayList<WindowContainer> all = new ArrayList<>(opening);
+            all.addAll(closing);
+            for (WindowContainer app : all) {
+                if (app.hasChild(mNavigatingWindow)) {
+                    EventLogTags.writeWmBackNaviCanceled("transitionHappens");
+                    mObserver.sendResult(null /* result */);
+                    break;
+                }
+            }
+
+        }
+
+        private boolean atSameDisplay(WindowState newFocus) {
+            final int navigatingDisplayId = mNavigatingWindow.getDisplayId();
+            return newFocus == null || newFocus.getDisplayId() == navigatingDisplayId;
+        }
     }
 
     // For shell transition
@@ -403,8 +483,7 @@
      *  animations, and shouldn't join next transition.
      */
     boolean containsBackAnimationTargets(Transition transition) {
-        if (!mAnimationHandler.mComposed
-                || (transition.mType != TRANSIT_CLOSE && transition.mType != TRANSIT_TO_BACK)) {
+        if (!isMonitoringTransition()) {
             return false;
         }
         final ArraySet<WindowContainer> targets = transition.mParticipants;
@@ -420,19 +499,19 @@
                 mTmpCloseApps.add(wc);
             }
         }
-        final boolean result = mAnimationHandler.containsBackAnimationTargets(
-                mTmpOpenApps, mTmpCloseApps);
-        if (result) {
-            mAnimationHandler.mOpenTransitionTargetMatch =
-                    mAnimationHandler.containTarget(mTmpOpenApps, true);
+        final boolean matchAnimationTargets = isWaitBackTransition()
+                && (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK)
+                && mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
+        if (!matchAnimationTargets) {
+            mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
         }
         mTmpOpenApps.clear();
         mTmpCloseApps.clear();
-        return result;
+        return matchAnimationTargets;
     }
 
     boolean isMonitorTransitionTarget(WindowContainer wc) {
-        if (!mAnimationHandler.mComposed || !mAnimationHandler.mWaitTransition) {
+        if (!isWaitBackTransition()) {
             return false;
         }
         return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
@@ -951,16 +1030,13 @@
         }
     }
 
-    private void onBackNavigationDone(Bundle result, WindowState focusedWindow, int backType) {
+    private void onBackNavigationDone(Bundle result, int backType) {
         boolean triggerBack = result != null && result.getBoolean(
                 BackNavigationInfo.KEY_TRIGGER_BACK);
         ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
                 + "triggerBack=%b", backType, triggerBack);
 
-        if (mFocusObserver != null) {
-            focusedWindow.unregisterFocusObserver(mFocusObserver);
-            mFocusObserver = null;
-        }
+        mNavigationMonitor.stopMonitor();
         mBackAnimationInProgress = false;
         mShowWallpaper = false;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index dfa1a62..86c4e0f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3809,6 +3809,7 @@
         }
 
         getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
+        mAtmService.mBackNavigationController.onFocusChanged(newFocus);
 
         if (imWindowChanged && oldFocus != mInputMethodWindow) {
             // Focus of the input method window changed. Perform layout if needed.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a15967bb..ce3379e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2590,6 +2590,7 @@
         lp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+        lp.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setFitInsetsTypes(0);
         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         if (ActivityManager.isHighEndGfx()) {
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 031e022..594929b 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -66,6 +66,8 @@
 # bootanim finished:
 31007 wm_boot_animation_done (time|2|3)
 
+# Back navigation.
+31100 wm_back_navi_canceled (Reason|3)
 
 # IME surface parent is updated.
 32003 imf_update_ime_parent (surface name|3)
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 8cbd553..3cec3aa 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1225,7 +1225,7 @@
 
         // Clear last paused activity if focused root task changed while sleeping, so that the
         // top activity of current focused task can be resumed.
-        if (mDisplayContent.isSleeping()) {
+        if (mDisplayContent.isSleeping() && currentFocusedTask != null) {
             currentFocusedTask.clearLastPausedActivity();
         }
 
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index 3d504ef..9c50a5a 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -49,11 +49,14 @@
     /** Represents the results of a given query into the registry. */
     public static final class FilterResult {
         final String mPackageName;
+        final String mFlattenedRequest;
         final List<CredentialEntry> mCredentialEntries;
 
         private FilterResult(String packageName,
+                String flattenedRequest,
                 List<CredentialEntry> credentialEntries) {
             mPackageName = packageName;
+            mFlattenedRequest = flattenedRequest;
             mCredentialEntries = credentialEntries;
         }
     }
@@ -133,12 +136,13 @@
     /** Returns package names and entries of a CredentialProviders that can satisfy a given
      * {@link CredentialDescription}. */
     public Set<FilterResult> getFilteredResultForProvider(String packageName,
-            List<String> flatRequestStrings) {
+            String flatRequestStrings) {
         Set<FilterResult> result = new HashSet<>();
         Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
         for (CredentialDescription containedDescription: currentSet) {
-            if (flatRequestStrings.contains(containedDescription.getFlattenedRequestString())) {
-                result.add(new FilterResult(packageName, containedDescription
+            if (flatRequestStrings.equals(containedDescription.getFlattenedRequestString())) {
+                result.add(new FilterResult(packageName,
+                        containedDescription.getFlattenedRequestString(), containedDescription
                         .getCredentialEntries()));
             }
         }
@@ -147,13 +151,15 @@
 
     /** Returns package names of CredentialProviders that can satisfy a given
      * {@link CredentialDescription}. */
-    public Set<String> getMatchingProviders(Set<String> flatRequestString) {
-        Set<String> result = new HashSet<>();
+    public Set<FilterResult> getMatchingProviders(Set<String> flatRequestString) {
+        Set<FilterResult> result = new HashSet<>();
         for (String packageName: mCredentialDescriptions.keySet()) {
             Set<CredentialDescription> currentSet = mCredentialDescriptions.get(packageName);
             for (CredentialDescription containedDescription : currentSet) {
                 if (flatRequestString.contains(containedDescription.getFlattenedRequestString())) {
-                    result.add(packageName);
+                    result.add(new FilterResult(packageName,
+                            containedDescription.getFlattenedRequestString(), containedDescription
+                            .getCredentialEntries()));
                 }
             }
         }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index f3bbfb5..7a4e7df 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.credentials;
 
 import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
+import static android.Manifest.permission.LAUNCH_CREDENTIAL_SELECTOR;
 import static android.content.Context.CREDENTIAL_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
@@ -58,6 +59,7 @@
 import android.service.credentials.CredentialProviderInfo;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -66,6 +68,7 @@
 import com.android.server.infra.SecureSettingsServiceNameResolver;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -274,24 +277,25 @@
     // to be guarded by 'service.mLock', which is the same as mLock.
     private List<ProviderSession> initiateProviderSessionsWithActiveContainers(
             GetRequestSession session,
-            List<String> requestOptions,
-            Set<String> activeCredentialContainers) {
+            Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
+                    activeCredentialContainers) {
         List<ProviderSession> providerSessions = new ArrayList<>();
-        // Invoke all services of a user to initiate a provider session
-        for (String packageName : activeCredentialContainers) {
+        for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
+                activeCredentialContainers) {
             providerSessions.add(
                     ProviderRegistryGetSession.createNewSession(
                             mContext,
                             UserHandle.getCallingUserId(),
                             session,
-                            packageName,
-                            requestOptions));
+                            result.second.mPackageName,
+                            result.first));
         }
         return providerSessions;
     }
 
     @NonNull
-    private Set<String> getFilteredResultFromRegistry(List<CredentialOption> options) {
+    private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
+            getFilteredResultFromRegistry(List<CredentialOption> options) {
         // Session for active/provisioned credential descriptions;
         CredentialDescriptionRegistry registry =
                 CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
@@ -307,7 +311,22 @@
                         .collect(Collectors.toSet());
 
         // All requested credential descriptions based on the given request.
-        return registry.getMatchingProviders(requestedCredentialDescriptions);
+        Set<CredentialDescriptionRegistry.FilterResult> filterResults =
+                registry.getMatchingProviders(requestedCredentialDescriptions);
+
+        Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> result =
+                new HashSet<>();
+
+        for (CredentialDescriptionRegistry.FilterResult filterResult: filterResults) {
+            for (CredentialOption credentialOption: options) {
+                if (filterResult.mFlattenedRequest.equals(credentialOption
+                        .getCredentialRetrievalData()
+                        .getString(CredentialOption.FLATTENED_REQUEST))) {
+                    result.add(new Pair<>(credentialOption, filterResult));
+                }
+            }
+        }
+        return result;
     }
 
     @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
@@ -342,21 +361,22 @@
             int userId,
             @Nullable String origin) {
         final PackageInfo packageInfo;
-        String actualPackageName = origin == null ? realPackageName : origin;
+        CallingAppInfo callingAppInfo;
         try {
             packageInfo =
                 getContext()
                     .getPackageManager()
                     .getPackageInfoAsUser(
-                        actualPackageName,
+                            realPackageName,
                         PackageManager.PackageInfoFlags.of(
                             PackageManager.GET_SIGNING_CERTIFICATES),
                         userId);
+            callingAppInfo = new CallingAppInfo(realPackageName, packageInfo.signingInfo, origin);
         } catch (PackageManager.NameNotFoundException e) {
             Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage());
-            return new CallingAppInfo(actualPackageName, null);
+            callingAppInfo = new CallingAppInfo(realPackageName, null, origin);
         }
-        return new CallingAppInfo(actualPackageName, packageInfo.signingInfo);
+        return callingAppInfo;
     }
 
     final class CredentialManagerServiceStub extends ICredentialManager.Stub {
@@ -368,9 +388,15 @@
             Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
 
+            if (request.getOrigin() != null) {
+                // Check privileged permissions
+                mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
+            }
+
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
             enforceCallingPackage(callingPackage, callingUid);
+
             // New request session, scoped for this request only.
             final GetRequestSession session =
                     new GetRequestSession(
@@ -379,43 +405,13 @@
                             callingUid,
                             callback,
                             request,
-                            constructCallingAppInfo(callingPackage, userId, null),
+                            constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
                             CancellationSignal.fromTransport(cancelTransport));
 
             processGetCredential(request, callback, session);
             return cancelTransport;
         }
 
-        public ICancellationSignal executeGetCredentialWithOrigin(
-                GetCredentialRequest request,
-                IGetCredentialCallback callback,
-                final String callingPackage,
-                final String origin) {
-            Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
-            ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
-            // Check privileged permissions
-            mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
-
-            final int userId = UserHandle.getCallingUserId();
-            final int callingUid = Binder.getCallingUid();
-            enforceCallingPackage(callingPackage, callingUid);
-
-            // New request session, scoped for this request only.
-            final GetRequestSession session =
-                    new GetRequestSession(
-                        getContext(),
-                        userId,
-                        callingUid,
-                        callback,
-                        request,
-                        constructCallingAppInfo(callingPackage, userId, origin),
-                        CancellationSignal.fromTransport(cancelTransport));
-
-            processGetCredential(request, callback, session);
-            return cancelTransport;
-        }
-
         private void processGetCredential(
                 GetCredentialRequest request,
                 IGetCredentialCallback callback,
@@ -452,15 +448,6 @@
                 List<ProviderSession> sessionsWithoutRemoteService =
                         initiateProviderSessionsWithActiveContainers(
                         session,
-                        optionsThatRequireActiveCredentials.stream()
-                            .map(
-                                getCredentialOption ->
-                                    getCredentialOption
-                                        .getCredentialRetrievalData()
-                                        .getString(
-                                            CredentialOption
-                                                .FLATTENED_REQUEST))
-                            .collect(Collectors.toList()),
                         getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
 
                 List<ProviderSession> sessionsWithRemoteService =
@@ -510,6 +497,11 @@
                     + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
 
+            if (request.getOrigin() != null) {
+                // Check privileged permissions
+                mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
+            }
+
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
             enforceCallingPackage(callingPackage, callingUid);
@@ -522,43 +514,13 @@
                             callingUid,
                             request,
                             callback,
-                            constructCallingAppInfo(callingPackage, userId, null),
+                            constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
                             CancellationSignal.fromTransport(cancelTransport));
 
             processCreateCredential(request, callback, session);
             return cancelTransport;
         }
 
-        public ICancellationSignal executeCreateCredentialWithOrigin(
-                CreateCredentialRequest request,
-                ICreateCredentialCallback callback,
-                String callingPackage,
-                String origin) {
-            Log.i(TAG, "starting executeCreateCredential with callingPackage: " + callingPackage);
-            ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
-            // Check privileged permissions
-            mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
-
-            final int userId = UserHandle.getCallingUserId();
-            final int callingUid = Binder.getCallingUid();
-            enforceCallingPackage(callingPackage, callingUid);
-
-            // New request session, scoped for this request only.
-            final CreateRequestSession session =
-                    new CreateRequestSession(
-                        getContext(),
-                        userId,
-                        callingUid,
-                        request,
-                        callback,
-                        constructCallingAppInfo(callingPackage, userId, origin),
-                        CancellationSignal.fromTransport(cancelTransport));
-
-            processCreateCredential(request, callback, session);
-            return cancelTransport;
-        }
-
         private void processCreateCredential(
                 CreateCredentialRequest request,
                 ICreateCredentialCallback callback,
@@ -670,7 +632,8 @@
             }
 
             // Send an intent to the UI that we have new enabled providers.
-            getContext().sendBroadcast(IntentFactory.createProviderUpdateIntent());
+            getContext().sendBroadcast(IntentFactory.createProviderUpdateIntent(),
+                    LAUNCH_CREDENTIAL_SELECTOR);
         }
 
         @Override
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 5af8080..a57cb5f 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.credentials.CredentialOption;
 import android.credentials.GetCredentialException;
-import android.credentials.GetCredentialRequest;
 import android.credentials.GetCredentialResponse;
 import android.credentials.ui.Entry;
 import android.credentials.ui.GetCredentialProviderData;
@@ -49,7 +48,7 @@
  *
  * @hide
  */
-public class ProviderRegistryGetSession extends ProviderSession<GetCredentialRequest,
+public class ProviderRegistryGetSession extends ProviderSession<CredentialOption,
         Set<CredentialDescriptionRegistry.FilterResult>> {
 
     private static final String TAG = "ProviderRegistryGetSession";
@@ -62,15 +61,14 @@
             @UserIdInt int userId,
             @NonNull GetRequestSession getRequestSession,
             @NonNull String credentialProviderPackageName,
-            @NonNull List<String> requestOptions) {
+            @NonNull CredentialOption requestOption) {
         return new ProviderRegistryGetSession(
                 context,
                 userId,
                 getRequestSession,
-                getRequestSession.mClientRequest,
                 getRequestSession.mClientAppInfo,
                 credentialProviderPackageName,
-                requestOptions);
+                requestOption);
     }
 
     @NonNull
@@ -82,24 +80,22 @@
     @NonNull
     private final String mCredentialProviderPackageName;
     @NonNull
-    private final GetRequestSession mGetRequestSession;
-    @NonNull
-    private final List<String> mRequestOptions;
+    private final String mFlattenedRequestOptionString;
     private List<CredentialEntry> mCredentialEntries;
 
     protected ProviderRegistryGetSession(@NonNull Context context,
             @NonNull int userId,
             @NonNull GetRequestSession session,
-            @NonNull GetCredentialRequest request,
             @NonNull CallingAppInfo callingAppInfo,
             @NonNull String servicePackageName,
-            @NonNull List<String> requestOptions) {
-        super(context, null, request, session, userId, null);
-        mGetRequestSession = session;
+            @NonNull CredentialOption requestOption) {
+        super(context, null, requestOption, session, userId, null);
         mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId);
         mCallingAppInfo = callingAppInfo;
         mCredentialProviderPackageName = servicePackageName;
-        mRequestOptions = requestOptions;
+        mFlattenedRequestOptionString = requestOption
+                .getCredentialRetrievalData()
+                .getString(CredentialOption.FLATTENED_REQUEST);
     }
 
     private List<Entry> prepareUiCredentialEntries(
@@ -114,23 +110,18 @@
             Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
             credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
                     credentialEntry.getSlice(),
-                    setUpFillInIntent(credentialEntry.getType())));
+                    setUpFillInIntent()));
         }
         return credentialUiEntries;
     }
 
-    private Intent setUpFillInIntent(String type) {
+    private Intent setUpFillInIntent() {
         Intent intent = new Intent();
-        for (CredentialOption option : mProviderRequest.getCredentialOptions()) {
-            if (option.getType().equals(type)) {
-                intent.putExtra(
-                        CredentialProviderService
-                                .EXTRA_GET_CREDENTIAL_REQUEST,
-                        new android.service.credentials.GetCredentialRequest(
-                                mCallingAppInfo, option));
-                return intent;
-            }
-        }
+        intent.putExtra(
+                CredentialProviderService
+                        .EXTRA_GET_CREDENTIAL_REQUEST,
+                new android.service.credentials.GetCredentialRequest(
+                        mCallingAppInfo, mProviderRequest));
         return intent;
     }
 
@@ -176,11 +167,6 @@
 
     private void onCredentialEntrySelected(CredentialEntry credentialEntry,
             ProviderPendingIntentResponse providerPendingIntentResponse) {
-        if (!mCredentialEntries.contains(credentialEntry)) {
-            invokeCallbackWithError("",
-                    "");
-        }
-
         if (providerPendingIntentResponse != null) {
             // Check if pending intent has an error
             GetCredentialException exception = maybeGetPendingIntentException(
@@ -228,13 +214,13 @@
     protected void invokeSession() {
         mProviderResponse = mCredentialDescriptionRegistry
                 .getFilteredResultForProvider(mCredentialProviderPackageName,
-                        mRequestOptions);
+                        mFlattenedRequestOptionString);
         mCredentialEntries = mProviderResponse.stream().flatMap(
                         (Function<CredentialDescriptionRegistry.FilterResult,
                                 Stream<CredentialEntry>>) filterResult
                         -> filterResult.mCredentialEntries.stream())
                 .collect(Collectors.toList());
-        setStatus(Status.CREDENTIALS_RECEIVED);
+        updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
         // TODO(use metric later)
     }
 
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 0ca4dfc..17e1744 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -113,6 +113,7 @@
     private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
     @VisibleForTesting
     static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
+    private static final int DEBOUNCE_LENGTH_MS = 500;
 
     private final Context mContext;
     private final Injector mInjector;
@@ -129,6 +130,7 @@
     private final List<PeopleService.ConversationsListener> mConversationsListeners =
             new ArrayList<>(1);
     private final Handler mHandler;
+    private final PerPackageThrottler mShortcutsThrottler;
 
     private ContentObserver mCallLogContentObserver;
     private ContentObserver mMmsSmsContentObserver;
@@ -140,14 +142,17 @@
     private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver;
 
     public DataManager(Context context) {
-        this(context, new Injector(), BackgroundThread.get().getLooper());
+        this(context, new Injector(), BackgroundThread.get().getLooper(),
+                new PerPackageThrottlerImpl(BackgroundThread.getHandler(), DEBOUNCE_LENGTH_MS));
     }
 
-    DataManager(Context context, Injector injector, Looper looper) {
+    DataManager(Context context, Injector injector, Looper looper,
+            PerPackageThrottler shortcutsThrottler) {
         mContext = context;
         mInjector = injector;
         mScheduledExecutor = mInjector.createScheduledExecutor();
         mHandler = new Handler(looper);
+        mShortcutsThrottler = shortcutsThrottler;
     }
 
     /** Initialization. Called when the system services are up running. */
@@ -851,12 +856,12 @@
         // pair of <package name, conversation info>
         List<Pair<String, ConversationInfo>> cachedConvos = new ArrayList<>();
         userData.forAllPackages(packageData -> {
-                packageData.forAllConversations(conversationInfo -> {
-                    if (isEligibleForCleanUp(conversationInfo)) {
-                        cachedConvos.add(
-                                Pair.create(packageData.getPackageName(), conversationInfo));
-                    }
-                });
+            packageData.forAllConversations(conversationInfo -> {
+                if (isEligibleForCleanUp(conversationInfo)) {
+                    cachedConvos.add(
+                            Pair.create(packageData.getPackageName(), conversationInfo));
+                }
+            });
         });
         if (cachedConvos.size() <= targetCachedCount) {
             return;
@@ -867,8 +872,8 @@
                 numToUncache + 1,
                 Comparator.comparingLong((Pair<String, ConversationInfo> pair) ->
                         Math.max(
-                            pair.second.getLastEventTimestamp(),
-                            pair.second.getCreationTimestamp())).reversed());
+                                pair.second.getLastEventTimestamp(),
+                                pair.second.getCreationTimestamp())).reversed());
         for (Pair<String, ConversationInfo> cached : cachedConvos) {
             if (hasActiveNotifications(cached.first, userId, cached.second.getShortcutId())) {
                 continue;
@@ -1104,26 +1109,35 @@
         @Override
         public void onShortcutsAddedOrUpdated(@NonNull String packageName,
                 @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
-            mInjector.getBackgroundExecutor().execute(() -> {
-                PackageData packageData = getPackage(packageName, user.getIdentifier());
-                for (ShortcutInfo shortcut : shortcuts) {
-                    if (ShortcutHelper.isConversationShortcut(
-                            shortcut, mShortcutServiceInternal, user.getIdentifier())) {
-                        if (shortcut.isCached()) {
-                            ConversationInfo conversationInfo = packageData != null
-                                    ? packageData.getConversationInfo(shortcut.getId()) : null;
-                            if (conversationInfo == null
-                                    || !conversationInfo.isShortcutCachedForNotification()) {
-                                // This is a newly cached shortcut. Clean up the existing cached
-                                // shortcuts to ensure the cache size is under the limit.
-                                cleanupCachedShortcuts(user.getIdentifier(),
-                                        MAX_CACHED_RECENT_SHORTCUTS - 1);
+            mShortcutsThrottler.scheduleDebounced(
+                    new Pair<>(packageName, user.getIdentifier()),
+                    () -> {
+                        PackageData packageData = getPackage(packageName, user.getIdentifier());
+                        List<ShortcutInfo> queriedShortcuts = getShortcuts(packageName,
+                                user.getIdentifier(), null);
+                        boolean hasCachedShortcut = false;
+                        for (ShortcutInfo shortcut : queriedShortcuts) {
+                            if (ShortcutHelper.isConversationShortcut(
+                                    shortcut, mShortcutServiceInternal, user.getIdentifier())) {
+                                if (shortcut.isCached()) {
+                                    ConversationInfo info = packageData != null
+                                            ? packageData.getConversationInfo(shortcut.getId())
+                                            : null;
+                                    if (info == null
+                                            || !info.isShortcutCachedForNotification()) {
+                                        hasCachedShortcut = true;
+                                    }
+                                }
+                                addOrUpdateConversationInfo(shortcut);
                             }
                         }
-                        addOrUpdateConversationInfo(shortcut);
-                    }
-                }
-            });
+                        // Added at least one new conversation. Uncache older existing cached
+                        // shortcuts to ensure the cache size is under the limit.
+                        if (hasCachedShortcut) {
+                            cleanupCachedShortcuts(user.getIdentifier(),
+                                    MAX_CACHED_RECENT_SHORTCUTS);
+                        }
+                    });
         }
 
         @Override
diff --git a/services/people/java/com/android/server/people/data/PerPackageThrottler.java b/services/people/java/com/android/server/people/data/PerPackageThrottler.java
new file mode 100644
index 0000000..3d6cd84
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/PerPackageThrottler.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.util.Pair;
+
+/** The interface for throttling expensive runnables per package. */
+interface PerPackageThrottler {
+    /**
+     * Schedule a runnable to run in the future, and debounce runnables for same {@code pkgUserId}
+     * that occur until that future has run.
+     */
+    void scheduleDebounced(Pair<String, Integer> pkgUserId, Runnable runnable);
+}
diff --git a/services/people/java/com/android/server/people/data/PerPackageThrottlerImpl.java b/services/people/java/com/android/server/people/data/PerPackageThrottlerImpl.java
new file mode 100644
index 0000000..fa5a67b
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/PerPackageThrottlerImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.os.Handler;
+import android.util.Pair;
+
+import java.util.HashSet;
+
+/**
+ * A class that implements a per-package throttler that prevents a runnable from executing more than
+ * once every {@code debounceTime}.
+ */
+public class PerPackageThrottlerImpl implements PerPackageThrottler {
+    private final Handler mBackgroundHandler;
+    private final int mDebounceTime;
+    private final HashSet<Pair<String, Integer>> mPkgScheduledTasks = new HashSet<>();
+
+    PerPackageThrottlerImpl(Handler backgroundHandler, int debounceTime) {
+        mBackgroundHandler = backgroundHandler;
+        mDebounceTime = debounceTime;
+    }
+
+    @Override
+    public synchronized void scheduleDebounced(
+            Pair<String, Integer> pkgUserId, Runnable runnable) {
+        if (mPkgScheduledTasks.contains(pkgUserId)) {
+            return;
+        }
+        mPkgScheduledTasks.add(pkgUserId);
+        mBackgroundHandler.postDelayed(() -> {
+            synchronized (this) {
+                mPkgScheduledTasks.remove(pkgUserId);
+                runnable.run();
+            }
+        }, mDebounceTime);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/ExpectableTestCase.java b/services/tests/mockingservicestests/src/com/android/server/ExpectableTestCase.java
new file mode 100644
index 0000000..a329f5a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/ExpectableTestCase.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import com.google.common.annotations.GwtIncompatible;
+import com.google.common.base.Optional;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Table;
+import com.google.common.truth.BigDecimalSubject;
+import com.google.common.truth.BooleanSubject;
+import com.google.common.truth.ClassSubject;
+import com.google.common.truth.ComparableSubject;
+import com.google.common.truth.DoubleSubject;
+import com.google.common.truth.Expect;
+import com.google.common.truth.FloatSubject;
+import com.google.common.truth.GuavaOptionalSubject;
+import com.google.common.truth.IntegerSubject;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.LongSubject;
+import com.google.common.truth.MapSubject;
+import com.google.common.truth.MultimapSubject;
+import com.google.common.truth.MultisetSubject;
+import com.google.common.truth.ObjectArraySubject;
+import com.google.common.truth.PrimitiveBooleanArraySubject;
+import com.google.common.truth.PrimitiveByteArraySubject;
+import com.google.common.truth.PrimitiveCharArraySubject;
+import com.google.common.truth.PrimitiveDoubleArraySubject;
+import com.google.common.truth.PrimitiveFloatArraySubject;
+import com.google.common.truth.PrimitiveIntArraySubject;
+import com.google.common.truth.PrimitiveLongArraySubject;
+import com.google.common.truth.PrimitiveShortArraySubject;
+import com.google.common.truth.StandardSubjectBuilder;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+import com.google.common.truth.TableSubject;
+import com.google.common.truth.ThrowableSubject;
+
+import org.junit.Rule;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+// NOTE: it could be a more generic AbstractTruthTestCase that provide similar methods
+// for assertThat() / assertWithMessage(), but then we'd need to remove all static import imports
+// from classes that indirectly extend it.
+/**
+ * Base class to make it easier to use {@code Truth} {@link Expect} assertions.
+ */
+public abstract class ExpectableTestCase {
+
+    @Rule
+    public final Expect mExpect = Expect.create();
+
+    protected final StandardSubjectBuilder expectWithMessage(String msg) {
+        return mExpect.withMessage(msg);
+    }
+
+    protected final StandardSubjectBuilder expectWithMessage(String format, Object...args) {
+        return mExpect.withMessage(format, args);
+    }
+
+    protected final <ComparableT extends Comparable<?>> ComparableSubject<ComparableT> expectThat(
+            ComparableT actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final BigDecimalSubject expectThat(BigDecimal actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final Subject expectThat(Object actual) {
+        return mExpect.that(actual);
+    }
+
+    @GwtIncompatible("ClassSubject.java")
+    protected final ClassSubject expectThat(Class<?> actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final ThrowableSubject expectThat(Throwable actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final LongSubject expectThat(Long actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final DoubleSubject expectThat(Double actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final FloatSubject expectThat(Float actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final IntegerSubject expectThat(Integer actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final BooleanSubject expectThat(Boolean actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final StringSubject expectThat(String actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final IterableSubject expectThat(Iterable<?> actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final <T> ObjectArraySubject<T> expectThat(T[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveBooleanArraySubject expectThat(boolean[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveShortArraySubject expectThat(short[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveIntArraySubject expectThat(int[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveLongArraySubject expectThat(long[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveCharArraySubject expectThat(char[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveByteArraySubject expectThat(byte[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveFloatArraySubject expectThat(float[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final PrimitiveDoubleArraySubject expectThat(double[] actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final GuavaOptionalSubject expectThat(Optional<?> actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final MapSubject expectThat(Map<?, ?> actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final MultimapSubject expectThat(Multimap<?, ?> actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final MultisetSubject expectThat(Multiset<?> actual) {
+        return mExpect.that(actual);
+    }
+
+    protected final TableSubject expectThat(Table<?, ?, ?> actual) {
+        return mExpect.that(actual);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoRule.java b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoRule.java
new file mode 100644
index 0000000..881dd50
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoRule.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.internal.util.Preconditions;
+import com.android.modules.utils.testing.StaticMockFixture;
+import com.android.modules.utils.testing.StaticMockFixtureRule;
+
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Rule to make it easier to use Extended Mockito.
+ *
+ * <p>It's derived from {@link StaticMockFixtureRule}, with the additional features:
+ *
+ * <ul>
+ *   <li>Easier to define which classes must be statically mocked or spied
+ *   <li>Automatically starts mocking (so tests don't need a mockito runner or rule)
+ *   <li>Automatically clears the inlined mocks at the end (to avoid OOM)
+ *   <li>Allows other customization like strictness
+ * </ul>
+ */
+public final class ExtendedMockitoRule extends StaticMockFixtureRule {
+
+    private static final String TAG = ExtendedMockitoRule.class.getSimpleName();
+
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final Object mTestClassInstance;
+    private final Strictness mStrictness;
+
+    private ExtendedMockitoRule(Builder builder) {
+        super(() -> new SimpleStatickMockFixture(builder.mMockedStaticClasses,
+                builder.mSpiedStaticClasses, builder.mDynamicSessionBuilderConfigurator,
+                builder.mAfterSessionFinishedCallback));
+        mTestClassInstance = builder.mTestClassInstance;
+        mStrictness = builder.mStrictness;
+        if (VERBOSE) {
+            Log.v(TAG, "strictness=" + mStrictness + ", testClassInstance" + mTestClassInstance
+                    + ", mockedStaticClasses=" + builder.mMockedStaticClasses
+                    + ", spiedStaticClasses=" + builder.mSpiedStaticClasses
+                    + ", dynamicSessionBuilderConfigurator="
+                    + builder.mDynamicSessionBuilderConfigurator
+                    + ", afterSessionFinishedCallback=" + builder.mAfterSessionFinishedCallback);
+        }
+    }
+
+    @Override
+    public StaticMockitoSessionBuilder getSessionBuilder() {
+        StaticMockitoSessionBuilder sessionBuilder = super.getSessionBuilder();
+        if (mStrictness != null) {
+            if (VERBOSE) {
+                Log.v(TAG, "Setting strictness to " + mStrictness + " on " + sessionBuilder);
+            }
+            sessionBuilder.strictness(mStrictness);
+        }
+        return sessionBuilder.initMocks(mTestClassInstance);
+    }
+
+    public static final class Builder {
+        private final Object mTestClassInstance;
+        private @Nullable Strictness mStrictness;
+        private final List<Class<?>> mMockedStaticClasses = new ArrayList<>();
+        private final List<Class<?>> mSpiedStaticClasses = new ArrayList<>();
+        private @Nullable Visitor<StaticMockitoSessionBuilder> mDynamicSessionBuilderConfigurator;
+        private @Nullable Runnable mAfterSessionFinishedCallback;
+
+        public Builder(Object testClassInstance) {
+            mTestClassInstance = Objects.requireNonNull(testClassInstance);
+        }
+
+        public Builder setStrictness(Strictness strictness) {
+            mStrictness = Objects.requireNonNull(strictness);
+            return this;
+        }
+
+        public Builder mockStatic(Class<?> clazz) {
+            Objects.requireNonNull(clazz);
+            Preconditions.checkState(!mMockedStaticClasses.contains(clazz),
+                    "class %s already mocked", clazz);
+            mMockedStaticClasses.add(clazz);
+            return this;
+        }
+
+        public Builder spyStatic(Class<?> clazz) {
+            Objects.requireNonNull(clazz);
+            Preconditions.checkState(!mSpiedStaticClasses.contains(clazz),
+                    "class %s already spied", clazz);
+            mSpiedStaticClasses.add(clazz);
+            return this;
+        }
+
+        public Builder dynamiclyConfigureSessionBuilder(
+                Visitor<StaticMockitoSessionBuilder> dynamicSessionBuilderConfigurator) {
+            mDynamicSessionBuilderConfigurator = Objects
+                    .requireNonNull(dynamicSessionBuilderConfigurator);
+            return this;
+        }
+
+        public Builder afterSessionFinished(Runnable runnable) {
+            mAfterSessionFinishedCallback = Objects.requireNonNull(runnable);
+            return this;
+        }
+
+        public ExtendedMockitoRule build() {
+            return new ExtendedMockitoRule(this);
+        }
+    }
+
+    private static final class SimpleStatickMockFixture implements StaticMockFixture {
+
+        private final List<Class<?>> mMockedStaticClasses;
+        private final List<Class<?>> mSpiedStaticClasses;
+        @Nullable
+        private final Visitor<StaticMockitoSessionBuilder> mDynamicSessionBuilderConfigurator;
+        @Nullable
+        private final Runnable mAfterSessionFinishedCallback;
+
+        private SimpleStatickMockFixture(List<Class<?>> mockedStaticClasses,
+                List<Class<?>> spiedStaticClasses,
+                @Nullable Visitor<StaticMockitoSessionBuilder> dynamicSessionBuilderConfigurator,
+                @Nullable Runnable afterSessionFinishedCallback) {
+            mMockedStaticClasses = mockedStaticClasses;
+            mSpiedStaticClasses = spiedStaticClasses;
+            mDynamicSessionBuilderConfigurator = dynamicSessionBuilderConfigurator;
+            mAfterSessionFinishedCallback = afterSessionFinishedCallback;
+        }
+
+        @Override
+        public StaticMockitoSessionBuilder setUpMockedClasses(
+                StaticMockitoSessionBuilder sessionBuilder) {
+            mMockedStaticClasses.forEach((c) -> sessionBuilder.mockStatic(c));
+            mSpiedStaticClasses.forEach((c) -> sessionBuilder.spyStatic(c));
+            if (mDynamicSessionBuilderConfigurator != null) {
+                mDynamicSessionBuilderConfigurator.visit(sessionBuilder);
+            }
+            return sessionBuilder;
+        }
+
+        @Override
+        public void setUpMockBehaviors() {
+        }
+
+        @Override
+        public void tearDown() {
+            try {
+                if (mAfterSessionFinishedCallback != null) {
+                    mAfterSessionFinishedCallback.run();
+                }
+            } finally {
+                if (VERBOSE) {
+                    Log.v(TAG, "calling Mockito.framework().clearInlineMocks()");
+                }
+                // When using inline mock maker, clean up inline mocks to prevent OutOfMemory
+                // errors. See https://github.com/mockito/mockito/issues/1614 and b/259280359.
+                Mockito.framework().clearInlineMocks();
+            }
+        }
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java b/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
deleted file mode 100644
index 48483a1..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/ExtendedMockitoTestCase.java
+++ /dev/null
@@ -1,249 +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.server;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import android.annotation.Nullable;
-import android.util.Log;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import com.google.common.truth.Expect;
-import com.google.common.truth.StandardSubjectBuilder;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.rules.RuleChain;
-import org.mockito.Mockito;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-import java.lang.reflect.Constructor;
-
-/**
- * Base class to make it easier to write tests that uses {@code ExtendedMockito}.
- *
- */
-public abstract class ExtendedMockitoTestCase {
-
-    private static final String TAG = ExtendedMockitoTestCase.class.getSimpleName();
-
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
-    /**
-     * Number of invocations, used to force a failure on {@link #forceFailure(int, Class, String)}.
-     */
-    private static int sInvocationsCounter;
-
-    /**
-     * Sessions follow the "Highlander Rule": There can be only one!
-     *
-     * <p>So, we keep track of that and force-close it if needed.
-     */
-    @Nullable
-    private static MockitoSession sHighlanderSession;
-
-    /**
-     * Points to where the current session was created.
-     */
-    private static Exception sSessionCreationLocation;
-
-    private MockitoSession mSession;
-
-    protected final Expect mExpect = Expect.create();
-    protected final DumpableDumperRule mDumpableDumperRule = new DumpableDumperRule();
-
-    @Rule
-    public final RuleChain mTwoRingsOfPowerAndOneChainToRuleThemAll = RuleChain
-            .outerRule(mDumpableDumperRule)
-            .around(mExpect);
-
-    public ExtendedMockitoTestCase() {
-        sInvocationsCounter++;
-    }
-
-    @Before
-    public final void startSession() {
-        if (VERBOSE) {
-            Log.v(TAG, "startSession() for " + getTestName() + " on thread "
-                    + Thread.currentThread() + "; sHighlanderSession=" + sHighlanderSession);
-        }
-        createSessionLocation();
-        finishHighlanderSessionIfNeeded("startSession()");
-        StaticMockitoSessionBuilder builder = mockitoSession()
-                .initMocks(this)
-                .strictness(getSessionStrictness());
-        initializeSession(builder);
-        sHighlanderSession = mSession = builder.startMocking();
-    }
-
-    private void createSessionLocation() {
-        try {
-            sSessionCreationLocation = new Exception(getTestName());
-        } catch (Exception e) {
-            // Better safe than sorry...
-            Log.e(TAG, "Could not create sSessionCreationLocation with " + getTestName()
-                    + " on thread " + Thread.currentThread(), e);
-            sSessionCreationLocation = e;
-        }
-    }
-
-    /**
-     * Gets the session strictness.
-     *
-     * @return {@link Strictness.LENIENT} by default; subclasses can override.
-     */
-    protected Strictness getSessionStrictness() {
-        return Strictness.LENIENT;
-    }
-
-    /**
-     * Initializes the mockito session.
-     *
-     * <p>Typically used to define which classes should have static methods mocked or spied.
-     */
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        if (VERBOSE) {
-            Log.v(TAG, "initializeSession()");
-        }
-    }
-
-    @After
-    public final void finishSession() throws Exception {
-        if (false) { // For obvious reasons, should NEVER be merged as true
-            forceFailure(1, RuntimeException.class, "to simulate an unfinished session");
-        }
-
-        // mSession.finishMocking() must ALWAYS be called (hence the over-protective try/finally
-        // statements), otherwise it would cause failures on future tests as mockito
-        // cannot start a session when a previous one is not finished
-        try {
-            if (VERBOSE) {
-                Log.v(TAG, "finishSession() for " + getTestName() + " on thread "
-                        + Thread.currentThread() + "; sHighlanderSession=" + sHighlanderSession);
-
-            }
-        } finally {
-            sHighlanderSession = null;
-            finishSessionMocking();
-            afterSessionFinished();
-        }
-    }
-
-    /**
-     * Called after the mockito session was finished
-     *
-     * <p>This method should be used by subclasses that MUST do their cleanup after the session is
-     * finished (as methods marked with {@link After} in the subclasses would be called BEFORE
-     * that).
-     */
-    protected void afterSessionFinished() {
-        if (VERBOSE) {
-            Log.v(TAG, "afterSessionFinished()");
-        }
-    }
-
-    private void finishSessionMocking() {
-        if (mSession == null) {
-            Log.w(TAG, getClass().getSimpleName() + ".finishSession(): no session");
-            return;
-        }
-        try {
-            mSession.finishMocking();
-        } finally {
-            // Shouldn't need to set mSession to null as JUnit always instantiate a new object,
-            // but it doesn't hurt....
-            mSession = null;
-            // When using inline mock maker, clean up inline mocks to prevent OutOfMemory
-            // errors. See https://github.com/mockito/mockito/issues/1614 and b/259280359.
-            Mockito.framework().clearInlineMocks();
-        }
-    }
-
-    private void finishHighlanderSessionIfNeeded(String where) {
-        if (sHighlanderSession == null) {
-            if (VERBOSE) {
-                Log.v(TAG, "finishHighlanderSessionIfNeeded(): sHighlanderSession already null");
-            }
-            return;
-        }
-
-        if (sSessionCreationLocation != null) {
-            if (VERBOSE) {
-                Log.e(TAG, where + ": There can be only one! Closing unfinished session, "
-                        + "created at", sSessionCreationLocation);
-            } else {
-                Log.e(TAG, where + ": There can be only one! Closing unfinished session, "
-                        + "created at " +  sSessionCreationLocation);
-            }
-        } else {
-            Log.e(TAG, where + ": There can be only one! Closing unfinished session created at "
-                    + "unknown location");
-        }
-        try {
-            sHighlanderSession.finishMocking();
-        } catch (Throwable t) {
-            if (VERBOSE) {
-                Log.e(TAG, "Failed to close unfinished session on " + getTestName(), t);
-            } else {
-                Log.e(TAG, "Failed to close unfinished session on " + getTestName() + ": " + t);
-            }
-        } finally {
-            if (VERBOSE) {
-                Log.v(TAG, "Resetting sHighlanderSession at finishHighlanderSessionIfNeeded()");
-            }
-            sHighlanderSession = null;
-        }
-    }
-
-    /**
-     * Forces a failure at the given invocation of a test method by throwing an exception.
-     */
-    protected final <T extends Throwable> void forceFailure(int invocationCount,
-            Class<T> failureClass, String reason) throws T {
-        if (sInvocationsCounter != invocationCount) {
-            Log.d(TAG, "forceFailure(" + invocationCount + "): no-op on invocation #"
-                    + sInvocationsCounter);
-            return;
-        }
-        String message = "Throwing on invocation #" + sInvocationsCounter + ": " + reason;
-        Log.e(TAG, message);
-        T throwable;
-        try {
-            Constructor<T> constructor = failureClass.getConstructor(String.class);
-            throwable = constructor.newInstance("Throwing on invocation #" + sInvocationsCounter
-                    + ": " + reason);
-        } catch (Exception e) {
-            throw new IllegalArgumentException("Could not create exception of class " + failureClass
-                    + " using msg='" + message + "' as constructor");
-        }
-        throw throwable;
-    }
-
-    protected final @Nullable String getTestName() {
-        return mDumpableDumperRule.getTestName();
-    }
-
-    protected final StandardSubjectBuilder expectWithMessage(String msg) {
-        return mExpect.withMessage(msg);
-    }
-
-    protected final StandardSubjectBuilder expectWithMessage(String format, Object...args) {
-        return mExpect.withMessage(format, args);
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/Visitor.java b/services/tests/mockingservicestests/src/com/android/server/Visitor.java
new file mode 100644
index 0000000..447910e
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/Visitor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.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;
+
+/**
+ * Generic visitor.
+ */
+public interface Visitor<V> {
+    void visit(V visitee);
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 9dd2f82..b395f42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -159,7 +159,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.MockedVoidMethod;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
@@ -168,7 +167,7 @@
 import com.android.server.AppStateTracker;
 import com.android.server.AppStateTrackerImpl;
 import com.android.server.DeviceIdleInternal;
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.SystemClockTime.TimeConfidence;
 import com.android.server.SystemService;
@@ -182,6 +181,7 @@
 import libcore.util.EmptyArray;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
@@ -204,7 +204,7 @@
 @Presubmit
 @SuppressWarnings("GuardedBy")  // This test enforces synchronous behavior.
 @RunWith(AndroidJUnit4.class)
-public final class AlarmManagerServiceTest extends ExtendedMockitoTestCase {
+public final class AlarmManagerServiceTest {
     private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
     private static final int SYSTEM_UI_UID = 12345;
     private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
@@ -411,14 +411,9 @@
         }
     }
 
-    @Override
-    protected Strictness getSessionStrictness() {
-        return Strictness.WARN;
-    }
-
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .setStrictness(Strictness.WARN)
             .spyStatic(ActivityManager.class)
             .mockStatic(CompatChanges.class)
             .spyStatic(DateFormat.class)
@@ -431,8 +426,10 @@
             .mockStatic(ServiceManager.class)
             .mockStatic(Settings.Global.class)
             .mockStatic(SystemProperties.class)
-            .spyStatic(UserHandle.class);
-    }
+            .spyStatic(UserHandle.class)
+            .afterSessionFinished(
+                    () -> LocalServices.removeServiceForTest(AlarmManagerInternal.class))
+            .build();
 
     @Before
     public final void setUp() {
@@ -3805,9 +3802,4 @@
         mListener.removeListenerAlarmsForCachedUid(TEST_CALLING_UID_2);
         assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
     }
-
-    @Override
-    public void afterSessionFinished() {
-        LocalServices.removeServiceForTest(AlarmManagerInternal.class);
-    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
index e01a9a9..9ceb5e7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
@@ -31,11 +31,11 @@
 import android.util.Log;
 import android.view.Display;
 
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.am.ActivityManagerService.Injector;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 
@@ -45,7 +45,7 @@
  * Run as {@code atest
  * FrameworksMockingServicesTests:com.android.server.am.ActivityManagerServiceInjectorTest}
  */
-public final class ActivityManagerServiceInjectorTest extends ExtendedMockitoTestCase {
+public final class ActivityManagerServiceInjectorTest {
 
     private static final String TAG = ActivityManagerServiceInjectorTest.class.getSimpleName();
 
@@ -63,10 +63,10 @@
         when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager);
     }
 
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(UserManager.class);
-    }
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .spyStatic(UserManager.class)
+            .build();
 
     @Test
     public void testGetDisplayIdsForStartingBackgroundUsers_notSupported() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index dcdee37..95c2ed2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -78,18 +78,15 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.am.BroadcastQueueTest.SyncBarrier;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
@@ -98,8 +95,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(MockitoJUnitRunner.class)
-public class BroadcastQueueModernImplTest extends ExtendedMockitoTestCase {
+public final class BroadcastQueueModernImplTest {
     private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
     private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
 
@@ -118,15 +114,13 @@
 
     BroadcastProcessQueue mHead;
 
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(FrameworkStatsLog.class);
-    }
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .spyStatic(FrameworkStatsLog.class)
+            .build();
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
         mHandlerThread = new HandlerThread(getClass().getSimpleName());
         mHandlerThread.start();
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index f8cfdf1..ec9e5b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -40,9 +40,8 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 import com.android.modules.utils.testing.TestableDeviceConfig;
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
@@ -69,7 +68,7 @@
  * atest FrameworksMockingServicesTests:CachedAppOptimizerTest
  */
 @Presubmit
-public final class CachedAppOptimizerTest extends ExtendedMockitoTestCase {
+public final class CachedAppOptimizerTest {
 
     private ServiceThread mThread;
 
@@ -93,10 +92,11 @@
     public final ApplicationExitInfoTest.ServiceThreadRule
             mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
 
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        mDeviceConfig.setUpMockedClasses(builder);
-    }
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .dynamiclyConfigureSessionBuilder(
+                    sessionBuilder -> mDeviceConfig.setUpMockedClasses(sessionBuilder))
+            .build();
 
     @Before
     public void setUp() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
index 9f3bc33..b214787 100644
--- a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
@@ -29,7 +29,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.ExpectableTestCase;
 
 import libcore.io.Streams;
 
@@ -44,7 +44,7 @@
 import java.util.Objects;
 
 /** This class contains unit tests for the {@link CpuInfoReader}. */
-public final class CpuInfoReaderTest extends ExtendedMockitoTestCase {
+public final class CpuInfoReaderTest extends ExpectableTestCase {
     private static final String TAG = CpuInfoReaderTest.class.getSimpleName();
     private static final String ROOT_DIR_NAME = "CpuInfoReaderTest";
     private static final String VALID_CPUSET_DIR = "valid_cpuset";
@@ -426,7 +426,7 @@
                 .isNull();
     }
 
-    private static void compareCpuInfos(String message,
+    private void compareCpuInfos(String message,
             SparseArray<CpuInfoReader.CpuInfo> expected,
             SparseArray<CpuInfoReader.CpuInfo> actual) {
         assertWithMessage("%s. Total CPU infos", message).that(actual.size())
@@ -435,7 +435,7 @@
             int cpuCoreId = expected.keyAt(i);
             CpuInfoReader.CpuInfo expectedCpuInfo = expected.valueAt(i);
             CpuInfoReader.CpuInfo actualCpuInfo = actual.get(cpuCoreId);
-            assertWithMessage("%s. Core %d's CPU info", message, cpuCoreId).that(actualCpuInfo)
+            expectWithMessage("%s. Core %s's CPU info", message, cpuCoreId).that(actualCpuInfo)
                     .isEqualTo(expectedCpuInfo);
         }
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
index 7ab1363..49a2cc6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
@@ -32,16 +32,16 @@
 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.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 
-public final class CpuMonitorServiceTest extends ExtendedMockitoTestCase {
+public final class CpuMonitorServiceTest {
     private static final CpuAvailabilityMonitoringConfig TEST_CPU_AVAILABILITY_MONITORING_CONFIG =
             new CpuAvailabilityMonitoringConfig.Builder(CPUSET_ALL)
                     .addThreshold(30).addThreshold(70).build();
@@ -56,10 +56,10 @@
     private HandlerExecutor mHandlerExecutor;
     private CpuMonitorInternal mLocalService;
 
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.mockStatic(ServiceManager.class);
-    }
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .mockStatic(ServiceManager.class)
+            .build();
 
     @Before
     public void setUp() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 1ed2f78..564893c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -40,9 +40,8 @@
 
 import androidx.test.annotation.UiThreadTest;
 
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 import com.android.internal.widget.LockSettingsInternal;
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.am.UserState;
 import com.android.server.pm.UserManagerService.UserData;
@@ -50,13 +49,14 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 
 /**
  * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
  */
-public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
+public final class UserManagerServiceTest {
 
     private static final String TAG = UserManagerServiceTest.class.getSimpleName();
 
@@ -84,6 +84,13 @@
      */
     private static final int PROFILE_USER_ID = 643;
 
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .spyStatic(UserManager.class)
+            .spyStatic(LocalServices.class)
+            .mockStatic(Settings.Global.class)
+            .build();
+
     private final Object mPackagesLock = new Object();
     private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
             .getTargetContext();
@@ -109,14 +116,6 @@
      */
     private UserManagerInternal mUmi;
 
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder
-                .spyStatic(UserManager.class)
-                .spyStatic(LocalServices.class)
-                .mockStatic(Settings.Global.class);
-    }
-
     @Before
     @UiThreadTest // Needed to initialize main handler
     public void setFixtures() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
index 38cf634..9aa53db 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
@@ -20,8 +20,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
-import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
-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.android.server.pm.UserVisibilityChangedEvent.onInvisible;
 import static com.android.server.pm.UserVisibilityChangedEvent.onVisible;
@@ -128,7 +126,8 @@
 
         listener.verify();
     }
-  /* TODO: re-add
+
+  /* TODO(b/261538337): re-add after the reverted CL is merged again
 
     @Test
     public void
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 5176d68..1bf921c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -45,7 +45,8 @@
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
-import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.DumpableDumperRule;
+import com.android.server.ExpectableTestCase;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -64,7 +65,7 @@
  * is visible, display associated to the user, etc...) for each scenario (full user started on fg,
  * profile user started on bg, etc...).
  */
-abstract class UserVisibilityMediatorTestCase extends ExtendedMockitoTestCase {
+abstract class UserVisibilityMediatorTestCase extends ExpectableTestCase {
 
     private static final String TAG = UserVisibilityMediatorTestCase.class.getSimpleName();
 
@@ -125,6 +126,8 @@
         mBackgroundUserOnDefaultDisplayAllowed = backgroundUserOnDefaultDisplayAllowed;
     }
 
+    protected final DumpableDumperRule mDumpableDumperRule = new DumpableDumperRule();
+
     @Before
     public final void setFixtures() {
         mHandler = Handler.getMain();
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java
index cb59d37..02e46bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/SlogfTest.java
@@ -16,16 +16,9 @@
 
 package com.android.server.utils;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-
 import android.util.Log;
 import android.util.Slog;
 
@@ -51,7 +44,6 @@
         mSession = mockitoSession()
                 .initMocks(this)
                 .mockStatic(Slog.class)
-                .spyStatic(Slogf.class) // for isLoggable only
                 .strictness(Strictness.LENIENT)
                 .startMocking();
     }
@@ -66,11 +58,6 @@
     }
 
     @Test
-    public void testIsLoggable() {
-        assertThat(Slogf.isLoggable(TAG, Log.VERBOSE)).isEqualTo(Log.isLoggable(TAG, Log.VERBOSE));
-    }
-
-    @Test
     public void testV_msg() {
         Slogf.v(TAG, "msg");
 
@@ -85,42 +72,20 @@
     }
 
     @Test
-    public void testV_msgFormatted_enabled() {
-        enableLogging(Log.VERBOSE);
-
+    public void testV_msgFormatted() {
         Slogf.v(TAG, "msg in a %s", "bottle");
 
         verify(()-> Slog.v(TAG, "msg in a bottle"));
     }
 
     @Test
-    public void testV_msgFormatted_disabled() {
-        disableLogging(Log.VERBOSE);
-
-        Slogf.v(TAG, "msg in a %s", "bottle");
-
-        verify(()-> Slog.v(eq(TAG), any()), never());
-    }
-
-    @Test
-    public void testV_msgFormattedWithThrowable_enabled() {
-        enableLogging(Log.VERBOSE);
-
+    public void testV_msgFormattedWithThrowable() {
         Slogf.v(TAG, mThrowable, "msg in a %s", "bottle");
 
         verify(()-> Slog.v(TAG, "msg in a bottle", mThrowable));
     }
 
     @Test
-    public void testV_msgFormattedWithException_disabled() {
-        disableLogging(Log.VERBOSE);
-
-        Slogf.v(TAG, "msg in a %s", "bottle");
-
-        verify(()-> Slog.v(eq(TAG), any(String.class), any(Throwable.class)), never());
-    }
-
-    @Test
     public void testD_msg() {
         Slogf.d(TAG, "msg");
 
@@ -135,42 +100,20 @@
     }
 
     @Test
-    public void testD_msgFormatted_enabled() {
-        enableLogging(Log.DEBUG);
-
+    public void testD_msgFormatted() {
         Slogf.d(TAG, "msg in a %s", "bottle");
 
         verify(()-> Slog.d(TAG, "msg in a bottle"));
     }
 
     @Test
-    public void testD_msgFormatted_disabled() {
-        disableLogging(Log.DEBUG);
-
-        Slogf.d(TAG, "msg in a %s", "bottle");
-
-        verify(()-> Slog.d(eq(TAG), any()), never());
-    }
-
-    @Test
-    public void testD_msgFormattedWithThrowable_enabled() {
-        enableLogging(Log.DEBUG);
-
+    public void testD_msgFormattedWithThrowable() {
         Slogf.d(TAG, mThrowable, "msg in a %s", "bottle");
 
         verify(()-> Slog.d(TAG, "msg in a bottle", mThrowable));
     }
 
     @Test
-    public void testD_msgFormattedWithException_disabled() {
-        disableLogging(Log.DEBUG);
-
-        Slogf.d(TAG, mThrowable, "msg in a %s", "bottle");
-
-        verify(()-> Slog.d(eq(TAG), any(String.class), any(Throwable.class)), never());
-    }
-
-    @Test
     public void testI_msg() {
         Slogf.i(TAG, "msg");
 
@@ -185,42 +128,20 @@
     }
 
     @Test
-    public void testI_msgFormatted_enabled() {
-        enableLogging(Log.INFO);
-
+    public void testI_msgFormatted() {
         Slogf.i(TAG, "msg in a %s", "bottle");
 
         verify(()-> Slog.i(TAG, "msg in a bottle"));
     }
 
     @Test
-    public void testI_msgFormatted_disabled() {
-        disableLogging(Log.INFO);
-
-        Slogf.i(TAG, "msg in a %s", "bottle");
-
-        verify(()-> Slog.i(eq(TAG), any()), never());
-    }
-
-    @Test
-    public void testI_msgFormattedWithThrowable_enabled() {
-        enableLogging(Log.INFO);
-
+    public void testI_msgFormattedWithThrowable() {
         Slogf.i(TAG, mThrowable, "msg in a %s", "bottle");
 
         verify(()-> Slog.i(TAG, "msg in a bottle", mThrowable));
     }
 
     @Test
-    public void testI_msgFormattedWithException_disabled() {
-        disableLogging(Log.INFO);
-
-        Slogf.i(TAG, mThrowable, "msg in a %s", "bottle");
-
-        verify(()-> Slog.i(eq(TAG), any(String.class), any(Throwable.class)), never());
-    }
-
-    @Test
     public void testW_msg() {
         Slogf.w(TAG, "msg");
 
@@ -242,42 +163,20 @@
     }
 
     @Test
-    public void testW_msgFormatted_enabled() {
-        enableLogging(Log.WARN);
-
+    public void testW_msgFormatted() {
         Slogf.w(TAG, "msg in a %s", "bottle");
 
         verify(()-> Slog.w(TAG, "msg in a bottle"));
     }
 
     @Test
-    public void testW_msgFormatted_disabled() {
-        disableLogging(Log.WARN);
-
-        Slogf.w(TAG, "msg in a %s", "bottle");
-
-        verify(()-> Slog.w(eq(TAG), any(String.class)), never());
-    }
-
-    @Test
-    public void testW_msgFormattedWithThrowable_enabled() {
-        enableLogging(Log.WARN);
-
+    public void testW_msgFormattedWithThrowable() {
         Slogf.w(TAG, mThrowable, "msg in a %s", "bottle");
 
         verify(()-> Slog.w(TAG, "msg in a bottle", mThrowable));
     }
 
     @Test
-    public void testW_msgFormattedWithException_disabled() {
-        disableLogging(Log.WARN);
-
-        Slogf.w(TAG, mThrowable, "msg in a %s", "bottle");
-
-        verify(()-> Slog.w(eq(TAG), any(String.class), any(Throwable.class)), never());
-    }
-
-    @Test
     public void testE_msg() {
         Slogf.e(TAG, "msg");
 
@@ -292,42 +191,20 @@
     }
 
     @Test
-    public void testE_msgFormatted_enabled() {
-        enableLogging(Log.ERROR);
-
+    public void testE_msgFormatted() {
         Slogf.e(TAG, "msg in a %s", "bottle");
 
         verify(()-> Slog.e(TAG, "msg in a bottle"));
     }
 
     @Test
-    public void testE_msgFormatted_disabled() {
-        disableLogging(Log.ERROR);
-
-        Slogf.e(TAG, "msg in a %s", "bottle");
-
-        verify(()-> Slog.e(eq(TAG), any()), never());
-    }
-
-    @Test
-    public void testE_msgFormattedWithThrowable_enabled() {
-        enableLogging(Log.ERROR);
-
+    public void testE_msgFormattedWithThrowable() {
         Slogf.e(TAG, mThrowable, "msg in a %s", "bottle");
 
         verify(()-> Slog.e(TAG, "msg in a bottle", mThrowable));
     }
 
     @Test
-    public void testE_msgFormattedWithException_disabled() {
-        disableLogging(Log.ERROR);
-
-        Slogf.e(TAG, mThrowable, "msg in a %s", "bottle");
-
-        verify(()-> Slog.e(eq(TAG), any(String.class), any(Throwable.class)), never());
-    }
-
-    @Test
     public void testWtf_msg() {
         Slogf.wtf(TAG, "msg");
 
@@ -382,16 +259,4 @@
 
         verify(()-> Slog.wtf(TAG, "msg in a bottle", mThrowable));
     }
-
-    private void enableLogging(@Log.Level int level) {
-        setIsLogging(level, true);
-    }
-
-    private void disableLogging(@Log.Level int level) {
-        setIsLogging(level, false);
-    }
-
-    private void setIsLogging(@Log.Level int level, boolean value) {
-        doReturn(value).when(() -> Slogf.isLoggable(TAG, level));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 4249405..503e579 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -284,7 +284,7 @@
         verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY),
                 eq(mTestableContext), anyInt(), any(), eq(mMockSecurityPolicy),
                 eq(mA11yms), eq(mA11yms.getTraceManager()),
-                eq(mMockWindowManagerService), eq(mMockA11yWindowManager));
+                eq(mMockWindowManagerService));
     }
 
     @SmallTest
@@ -296,7 +296,7 @@
         assertThrows(SecurityException.class,
                 () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
         verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
-                any(), any(), any(), any());
+                any(), any(), any());
     }
 
     @SmallTest
@@ -305,7 +305,7 @@
         assertThrows(IllegalArgumentException.class,
                 () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
         verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
-                any(), any(), any(), any());
+                any(), any(), any());
     }
 
     @SmallTest
@@ -314,7 +314,7 @@
         assertThrows(IllegalArgumentException.class,
                 () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
         verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
-                any(), any(), any(), any());
+                any(), any(), any());
     }
 
     @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 51d3bae..306ce4d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -44,6 +44,8 @@
 import android.graphics.PointF;
 import android.os.Handler;
 import android.os.Message;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.testing.TestableContext;
 import android.util.DebugUtils;
 import android.view.InputDevice;
@@ -507,6 +509,91 @@
         verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
     }
 
+    @Test
+    public void testTransitToPanningState_scaleDifferenceOverThreshold_startDetecting() {
+        final float scale = 2.0f;
+        final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+                .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+        final float persistedScale = (1.0f + threshold) * scale + 1.0f;
+        mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+                DEFAULT_Y, /* animate= */ false,
+                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+        mFullScreenMagnificationController.persistScale(DISPLAY_0);
+        mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
+                DEFAULT_Y, /* animate= */ false,
+                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+        mMgh.transitionTo(mMgh.mPanningScalingState);
+
+        assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+        mMgh.clearAndTransitionToStateDetecting();
+        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+    }
+
+    @Test
+    public void testTransitToPanningState_scaleDifferenceLessThanThreshold_doNotDetect() {
+        final float scale = 2.0f;
+        final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+                .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+        final float persistedScale = (1.0f + threshold) * scale - 0.1f;
+        mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+                DEFAULT_Y, /* animate= */ false,
+                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+        mFullScreenMagnificationController.persistScale(DISPLAY_0);
+        mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
+                DEFAULT_Y, /* animate= */ false,
+                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+        mMgh.transitionTo(mMgh.mPanningScalingState);
+
+        assertFalse(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+        mMgh.clearAndTransitionToStateDetecting();
+        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+    }
+
+    @Test
+    public void testPanningScaleToPersistedScale_detecting_vibrateAndClear() {
+        Vibrator vibrator = mock(Vibrator.class);
+        mContext.addMockSystemService(Vibrator.class, vibrator);
+
+        mMgh.mPanningScalingState.mDetectingPassPersistedScale = true;
+
+        final float persistedScale =
+                mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
+
+        mMgh.transitionTo(mMgh.mPanningScalingState);
+        mMgh.mPanningScalingState.setScaleAndClearIfNeeded(persistedScale, DEFAULT_X, DEFAULT_Y);
+
+        verify(vibrator).vibrate(any(VibrationEffect.class));
+        assertFalse(mMgh.mPanningScalingState.mScaling);
+
+        mMgh.clearAndTransitionToStateDetecting();
+        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+    }
+
+    @Test
+    public void testPanningScaleOverThreshold_notDetecting_startDetecting() {
+        final float persistedScale =
+                mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
+
+        mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+                DEFAULT_Y, /* animate= */ false,
+                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+        mMgh.transitionTo(mMgh.mPanningScalingState);
+
+        final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+                .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+        final float scale = (1.0f + threshold) * persistedScale + 1.0f;
+        mMgh.mPanningScalingState.setScaleAndClearIfNeeded(scale, DEFAULT_X, DEFAULT_Y);
+
+        assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+        mMgh.clearAndTransitionToStateDetecting();
+        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+    }
+
     private void assertActionsInOrder(List<MotionEvent> actualEvents,
             List<Integer> expectedActions) {
         assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index b0c3a6e..cf650d2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.biometrics.AuthenticateOptions;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.IBiometricService;
@@ -681,7 +682,37 @@
         TestableLooper.get(this).processAllMessages();
     }
 
-    private static class TestAuthenticationClient extends AuthenticationClient<Object> {
+    private static class TestAuthenticateOptions implements AuthenticateOptions {
+        @Override
+        public int getUserId() {
+            return 0;
+        }
+
+        @Override
+        public int getSensorId() {
+            return TEST_SENSOR_ID;
+        }
+
+        @Override
+        public int getDisplayState() {
+            return DISPLAY_STATE_UNKNOWN;
+        }
+
+        @NonNull
+        @Override
+        public String getOpPackageName() {
+            return "some.test.name";
+        }
+
+        @Nullable
+        @Override
+        public String getAttributionTag() {
+            return null;
+        }
+    }
+
+    private static class TestAuthenticationClient
+            extends AuthenticationClient<Object, TestAuthenticateOptions> {
         boolean mStartedHal = false;
         boolean mStoppedHal = false;
         boolean mDestroyed = false;
@@ -700,9 +731,10 @@
                 @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token,
                 @NonNull ClientMonitorCallbackConverter listener, int cookie,
                 @NonNull BiometricContext biometricContext) {
-            super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
-                    false /* restricted */, TAG, cookie, false /* requireConfirmation */,
-                    TEST_SENSOR_ID, mock(BiometricLogger.class), biometricContext,
+            super(context, lazyDaemon, token, listener, 0 /* operationId */,
+                    false /* restricted */, new TestAuthenticateOptions(), cookie,
+                    false /* requireConfirmation */,
+                    mock(BiometricLogger.class), biometricContext,
                     true /* isStrongBiometric */, null /* taskStackListener */,
                     null /* lockoutTracker */, false /* isKeyguard */,
                     true /* shouldVibrate */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 184a556..3ff802c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -33,6 +33,7 @@
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticateOptions;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -153,14 +154,18 @@
         when(mHal.getInterfaceVersion()).thenReturn(version);
 
         final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+        final FaceAuthenticateOptions options = new FaceAuthenticateOptions.Builder()
+                .setOpPackageName("test-owner")
+                .setUserId(5)
+                .setSensorId(9)
+                .build();
         return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
-                2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
-                false /* restricted */, "test-owner", 4 /* cookie */,
-                false /* requireConfirmation */, 9 /* sensorId */,
+                2 /* requestId */, mClientMonitorCallbackConverter, OP_ID,
+                false /* restricted */, options, 4 /* cookie */,
+                false /* requireConfirmation */,
                 mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
                 mUsageStats, null /* mLockoutCache */, false /* allowBackgroundAuthentication */,
-                null /* sensorPrivacyManager */,
-                0 /* biometricStrength */) {
+                null /* sensorPrivacyManager */, 0 /* biometricStrength */) {
             @Override
             protected ActivityTaskManager getActivityTaskManager() {
                 return mActivityTaskManager;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index e0fdb8c..c4c5505 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.hardware.biometrics.face.ISession;
+import android.hardware.face.FaceAuthenticateOptions;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -112,8 +113,13 @@
 
         final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
         return new FaceDetectClient(mContext, () -> aidl, mToken,
-                99 /* requestId */, mClientMonitorCallbackConverter, USER_ID,
-                "own-it", 5 /* sensorId */, mBiometricLogger, mBiometricContext,
+                99 /* requestId */, mClientMonitorCallbackConverter,
+                new FaceAuthenticateOptions.Builder()
+                        .setUserId(USER_ID)
+                        .setSensorId(5)
+                        .setOpPackageName("own-it")
+                        .build(),
+                mBiometricLogger, mBiometricContext,
                 false /* isStrongBiometric */, null /* sensorPrivacyManager */);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
index 3b66eab..54d6478 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
@@ -144,7 +144,7 @@
 
     @Test
     public void testAuthentication_enrollmentCallbackNeverNotified() {
-        AuthenticationClient<?> client = mock(AuthenticationClient.class);
+        AuthenticationClient<?, ?> client = mock(AuthenticationClient.class);
         mCallback.onClientFinished(client, true /* success */);
         verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
                 anyBoolean());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index a4048a2..25a700a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -16,24 +16,35 @@
 
 package com.android.server.biometrics.sensors.fingerprint;
 
+import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.app.AppOpsManager.OP_USE_BIOMETRIC;
+import static android.app.AppOpsManager.OP_USE_FINGERPRINT;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY;
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
+
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.IBiometricService;
-import android.hardware.biometrics.common.CommonProps;
-import android.hardware.biometrics.common.SensorStrength;
-import android.hardware.biometrics.fingerprint.FingerprintSensorType;
-import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.SensorLocation;
-import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.testing.TestableContext;
@@ -44,10 +55,13 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -64,6 +78,8 @@
     private static final int ID_VIRTUAL = 6;
     private static final String NAME_DEFAULT = "default";
     private static final String NAME_VIRTUAL = "virtual";
+    private static final List<FingerprintSensorPropertiesInternal> HIDL_AUTHENTICATORS =
+            List.of();
 
     @Rule
     public final MockitoRule mMockito = MockitoJUnit.rule();
@@ -74,47 +90,80 @@
     public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
 
     @Mock
+    private AppOpsManager mAppOpsManager;
+    @Mock
     private BiometricContext mBiometricContext;
     @Mock
     private IBiometricService mIBiometricService;
     @Mock
-    private IFingerprint mIFingerprintDefault;
+    private FingerprintProvider mFingerprintDefault;
     @Mock
-    private IFingerprint mIFingerprintVirtual;
+    private FingerprintProvider mFingerprintVirtual;
+    @Mock
+    private IFingerprintServiceReceiver mServiceReceiver;
+    @Mock
+    private IBinder mToken;
 
-    private final SensorProps mSensorPropsDefault = createProps(ID_DEFAULT,
-            SensorStrength.STRONG, FingerprintSensorType.POWER_BUTTON);
-    private final SensorProps mSensorPropsVirtual = createProps(ID_VIRTUAL,
-            SensorStrength.STRONG, FingerprintSensorType.UNDER_DISPLAY_OPTICAL);
+    @Captor
+    private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor;
+
+    private final FingerprintSensorPropertiesInternal mSensorPropsDefault =
+            new FingerprintSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG,
+                    2 /* maxEnrollmentsPerUser */,
+                    List.of(),
+                    TYPE_REAR,
+                    false /* resetLockoutRequiresHardwareAuthToken */);
+    private final FingerprintSensorPropertiesInternal mSensorPropsVirtual =
+            new FingerprintSensorPropertiesInternal(ID_VIRTUAL, STRENGTH_STRONG,
+                    2 /* maxEnrollmentsPerUser */,
+                    List.of(),
+                    TYPE_UDFPS_OPTICAL,
+                    false /* resetLockoutRequiresHardwareAuthToken */);
     private FingerprintService mService;
 
     @Before
     public void setup() throws Exception {
-        when(mIFingerprintDefault.getSensorProps()).thenReturn(
-                new SensorProps[]{mSensorPropsDefault});
-        when(mIFingerprintVirtual.getSensorProps()).thenReturn(
-                new SensorProps[]{mSensorPropsVirtual});
+        when(mFingerprintDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
+        when(mFingerprintVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
+        when(mFingerprintDefault.containsSensor(anyInt()))
+                .thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT));
+        when(mFingerprintVirtual.containsSensor(anyInt()))
+                .thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL));
 
-        mContext.getTestablePermissions().setPermission(
-                USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED);
+        mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
+        for (int permission : List.of(OP_USE_BIOMETRIC, OP_USE_FINGERPRINT)) {
+            when(mAppOpsManager.noteOp(eq(permission), anyInt(), any(), any(), any()))
+                    .thenReturn(AppOpsManager.MODE_ALLOWED);
+        }
+
+        for (String permission : List.of(USE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
+            mContext.getTestablePermissions().setPermission(
+                    permission, PackageManager.PERMISSION_GRANTED);
+        }
     }
 
     private void initServiceWith(String... aidlInstances) {
         mService = new FingerprintService(mContext, mBiometricContext,
                 () -> mIBiometricService,
                 () -> aidlInstances,
-                (fqName) -> {
-                    if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault;
-                    if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual;
+                (name) -> {
+                    if (NAME_DEFAULT.equals(name)) return mFingerprintDefault;
+                    if (NAME_VIRTUAL.equals(name)) return mFingerprintVirtual;
                     return null;
                 });
     }
 
+    private void initServiceWithAndWait(String... aidlInstances) throws Exception {
+        initServiceWith(aidlInstances);
+        mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
+        waitForRegistration();
+    }
+
     @Test
     public void registerAuthenticators_defaultOnly() throws Exception {
         initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
 
-        mService.mServiceWrapper.registerAuthenticators(List.of());
+        mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
         waitForRegistration();
 
         verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
@@ -126,7 +175,7 @@
         Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
                 Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
 
-        mService.mServiceWrapper.registerAuthenticators(List.of());
+        mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
         waitForRegistration();
 
         verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
@@ -136,7 +185,7 @@
     public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
         initServiceWith(NAME_VIRTUAL);
 
-        mService.mServiceWrapper.registerAuthenticators(List.of());
+        mService.mServiceWrapper.registerAuthenticators(HIDL_AUTHENTICATORS);
         waitForRegistration();
 
         verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
@@ -155,13 +204,47 @@
         latch.await(5, TimeUnit.SECONDS);
     }
 
-    private static SensorProps createProps(int id, byte strength, byte type) {
-        final SensorProps props = new SensorProps();
-        props.commonProps = new CommonProps();
-        props.commonProps.sensorId = id;
-        props.commonProps.sensorStrength = strength;
-        props.sensorType = type;
-        props.sensorLocations = new SensorLocation[]{new SensorLocation()};
-        return props;
+    @Test
+    public void authenticateWithDefaultSensorId() throws Exception {
+        initServiceWithAndWait(NAME_DEFAULT, NAME_VIRTUAL);
+
+        final long operationId = 2;
+        mService.mServiceWrapper.authenticate(mToken, operationId, mServiceReceiver,
+                new FingerprintAuthenticateOptions.Builder()
+                        .setSensorId(SENSOR_ID_ANY)
+                        .build());
+
+        final FingerprintAuthenticateOptions options =
+                verifyAuthenticateWithNewRequestId(mFingerprintDefault, operationId);
+        assertThat(options.getSensorId()).isEqualTo(ID_DEFAULT);
+        verifyNoAuthenticate(mFingerprintVirtual);
+    }
+
+
+    private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
+            FingerprintProvider provider, long operationId) {
+        return verifyAuthenticateWithNewRequestId(
+                provider, operationId, true /* shouldSchedule */);
+    }
+
+    private void verifyNoAuthenticate(FingerprintProvider provider) {
+        verifyAuthenticateWithNewRequestId(
+                provider, 0 /* operationId */, false /* shouldSchedule */);
+    }
+
+    private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
+            FingerprintProvider provider, long operationId, boolean shouldSchedule) {
+        verify(provider, shouldSchedule ? times(1) : never())
+                .scheduleAuthenticate(eq(mToken), eq(operationId), anyInt(), any(),
+                        mAuthenticateOptionsCaptor.capture(), anyBoolean(), anyInt(),
+                        anyBoolean());
+        verify(provider, never()).scheduleAuthenticate(eq(mToken), anyLong(),
+                anyInt(), any(), mAuthenticateOptionsCaptor.capture(), anyLong(),
+                anyBoolean(), anyInt(), anyBoolean());
+
+        if (shouldSchedule) {
+            return mAuthenticateOptionsCaptor.getValue();
+        }
+        return null;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 99f7905..f0f975c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -41,6 +41,7 @@
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -414,11 +415,16 @@
         when(mHal.getInterfaceVersion()).thenReturn(version);
 
         final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+        final FingerprintAuthenticateOptions options = new FingerprintAuthenticateOptions.Builder()
+                .setOpPackageName("test-owner")
+                .setUserId(5)
+                .setSensorId(9)
+                .build();
         return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken,
-                REQUEST_ID, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID,
-                false /* restricted */, "test-owner", 4 /* cookie */,
+                REQUEST_ID, mClientMonitorCallbackConverter, OP_ID,
+                false /* restricted */, options, 4 /* cookie */,
                 false /* requireConfirmation */,
-                9 /* sensorId */, mBiometricLogger, mBiometricContext,
+                mBiometricLogger, mBiometricContext,
                 true /* isStrongBiometric */,
                 null /* taskStackListener */, null /* lockoutCache */,
                 mUdfpsOverlayController, mSideFpsController, null, allowBackgroundAuthentication,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 2dbd8f6..e741e44 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -117,8 +118,13 @@
 
         final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
         return new FingerprintDetectClient(mContext, () -> aidl, mToken,
-                6 /* requestId */, mClientMonitorCallbackConverter, 2 /* userId */,
-                "a-test", 1 /* sensorId */, mBiometricLogger, mBiometricContext,
+                6 /* requestId */, mClientMonitorCallbackConverter,
+                new FingerprintAuthenticateOptions.Builder()
+                        .setUserId(2)
+                        .setSensorId(1)
+                        .setOpPackageName("a-test")
+                        .build(),
+                mBiometricLogger, mBiometricContext,
                 mUdfpsOverlayController, null, true /* isStrongBiometric */);
     }
 }
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 0cd50f0..cc6f7c2 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
@@ -35,6 +35,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -67,7 +68,11 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.hardware.Sensor;
+import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.IDisplayManager;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
 import android.hardware.input.IInputManager;
 import android.hardware.input.VirtualDpadConfig;
 import android.hardware.input.VirtualKeyEvent;
@@ -144,6 +149,7 @@
     private static final String DEVICE_NAME_3 = "device name 3";
     private static final int DISPLAY_ID_1 = 2;
     private static final int DISPLAY_ID_2 = 3;
+    private static final int NON_EXISTENT_DISPLAY_ID = 42;
     private static final int DEVICE_OWNER_UID_1 = 50;
     private static final int DEVICE_OWNER_UID_2 = 51;
     private static final int UID_1 = 0;
@@ -162,6 +168,8 @@
     private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000;
     private static final int VIRTUAL_DEVICE_ID_1 = 42;
     private static final int VIRTUAL_DEVICE_ID_2 = 43;
+    private static final VirtualDisplayConfig VIRTUAL_DISPLAY_CONFIG =
+            new VirtualDisplayConfig.Builder("virtual_display", 640, 480, 400).build();
     private static final VirtualDpadConfig DPAD_CONFIG =
             new VirtualDpadConfig.Builder()
                     .setVendorId(VENDOR_ID)
@@ -221,6 +229,8 @@
     @Mock
     private DisplayManagerInternal mDisplayManagerInternalMock;
     @Mock
+    private IDisplayManager mIDisplayManager;
+    @Mock
     private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
     @Mock
     private DevicePolicyManager mDevicePolicyManagerMock;
@@ -237,6 +247,8 @@
     @Mock
     private IVirtualDeviceSoundEffectListener mSoundEffectListener;
     @Mock
+    private IVirtualDisplayCallback mVirtualDisplayCallback;
+    @Mock
     private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
     @Mock
     private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
@@ -271,9 +283,13 @@
 
     private Intent createRestrictedActivityBlockedIntent(List displayCategories,
             String targetDisplayCategory) {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(displayCategories), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(),
+                eq(NONBLOCKED_APP_PACKAGE_NAME))).thenReturn(DISPLAY_ID_1);
+        VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("display", 640, 480,
+                420).setDisplayCategories(displayCategories).build();
+        mDeviceImpl.createVirtualDisplay(config, mVirtualDisplayCallback,
+                NONBLOCKED_APP_PACKAGE_NAME);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -327,6 +343,7 @@
         mContext = Mockito.spy(new ContextWrapper(
                 InstrumentationRegistry.getInstrumentation().getTargetContext()));
         doReturn(mContext).when(mContext).createContextAsUser(eq(Process.myUserHandle()), anyInt());
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any());
         when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
                 mDevicePolicyManagerMock);
 
@@ -369,15 +386,13 @@
 
     @Test
     public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
-        mDeviceImpl.mVirtualDisplayIds.remove(DISPLAY_ID_1);
-
-        assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
+        assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
                 .isEqualTo(DEVICE_ID_DEFAULT);
     }
 
     @Test
     public void getDeviceIdForDisplayId_withValidVirtualDisplayId_returnsDeviceId() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
 
         assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
                 .isEqualTo(mDeviceImpl.getDeviceId());
@@ -503,10 +518,9 @@
 
     @Test
     public void getDeviceIdsForUid_differentUidOnDevice_returnsNull() {
-        GenericWindowPolicyController gwpc =
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
-        gwpc.onRunningAppsChanged(Sets.newArraySet(UID_2));
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+                Sets.newArraySet(UID_2));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
         assertThat(deviceIds).isEmpty();
@@ -514,10 +528,9 @@
 
     @Test
     public void getDeviceIdsForUid_oneUidOnDevice_returnsCorrectId() {
-        GenericWindowPolicyController gwpc =
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
-        gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1));
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+                Sets.newArraySet(UID_1));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
         assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId());
@@ -525,10 +538,10 @@
 
     @Test
     public void getDeviceIdsForUid_twoUidsOnDevice_returnsCorrectId() {
-        GenericWindowPolicyController gwpc =
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
-        gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1, UID_2));
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+
+        mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+                Sets.newArraySet(UID_1, UID_2));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
         assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId());
@@ -538,11 +551,10 @@
     public void getDeviceIdsForUid_twoDevicesUidOnOne_returnsCorrectId() {
         VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
                 DEVICE_OWNER_UID_2);
+        addVirtualDisplay(secondDevice, DISPLAY_ID_2);
 
-        GenericWindowPolicyController gwpc =
-                secondDevice.createWindowPolicyController(new ArrayList<>());
-        secondDevice.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_2);
-        gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1));
+        secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
+                Sets.newArraySet(UID_1));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
         assertThat(deviceIds).containsExactly(secondDevice.getDeviceId());
@@ -550,16 +562,16 @@
 
     @Test
     public void getDeviceIdsForUid_twoDevicesUidOnBoth_returnsCorrectId() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
                 DEVICE_OWNER_UID_2);
-        GenericWindowPolicyController gwpc1 =
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        GenericWindowPolicyController gwpc2 =
-                secondDevice.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc1, DISPLAY_ID_1);
-        secondDevice.onVirtualDisplayCreatedLocked(gwpc2, DISPLAY_ID_2);
-        gwpc1.onRunningAppsChanged(Sets.newArraySet(UID_1));
-        gwpc2.onRunningAppsChanged(Sets.newArraySet(UID_1, UID_2));
+        addVirtualDisplay(secondDevice, DISPLAY_ID_2);
+
+
+        mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
+                Sets.newArraySet(UID_1));
+        secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
+                Sets.newArraySet(UID_1, UID_2));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
         assertThat(deviceIds).containsExactly(
@@ -568,8 +580,7 @@
 
     @Test
     public void getPreferredLocaleListForApp_keyboardAttached_returnLocaleHints() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
-
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
 
         mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1));
@@ -609,8 +620,8 @@
                         .setLanguageTag("fr-FR")
                         .build();
 
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
-        secondDevice.mVirtualDisplayIds.add(DISPLAY_ID_2);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(secondDevice, DISPLAY_ID_2);
 
         mDeviceImpl.createVirtualKeyboard(firstKeyboardConfig, BINDER);
         secondDevice.createVirtualKeyboard(secondKeyboardConfig, secondBinder);
@@ -640,10 +651,9 @@
 
     @Test
     public void onVirtualDisplayRemovedLocked_doesNotThrowException() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         // This call should not throw any exceptions.
-        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
+        mDeviceImpl.onVirtualDisplayRemoved(DISPLAY_ID_1);
     }
 
     @Test
@@ -659,8 +669,8 @@
     @Test
     public void onVirtualDisplayRemovedLocked_listenersNotified() {
         mLocalService.registerVirtualDisplayListener(mDisplayListener);
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
 
         mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1);
         TestableLooper.get(this).processAllMessages();
@@ -723,8 +733,7 @@
         verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
                 nullable(String.class), anyInt(), eq(null));
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
                 nullable(String.class), eq(DISPLAY_ID_1), eq(null));
@@ -733,12 +742,9 @@
     @Test
     public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()
             throws RemoteException {
-        GenericWindowPolicyController gwpc = mDeviceImpl.createWindowPolicyController(
-                new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         assertThrows(IllegalStateException.class,
-                () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1));
+                () -> addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1));
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
@@ -749,13 +755,12 @@
     public void onVirtualDisplayRemovedLocked_unknownDisplayId_throwsException() {
         final int unknownDisplayId = 999;
         assertThrows(IllegalStateException.class,
-                () -> mDeviceImpl.onVirtualDisplayRemovedLocked(unknownDisplayId));
+                () -> mDeviceImpl.onVirtualDisplayRemoved(unknownDisplayId));
     }
 
     @Test
     public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
@@ -764,14 +769,13 @@
                 nullable(String.class), eq(DISPLAY_ID_1), eq(null));
 
         IBinder wakeLock = wakeLockCaptor.getValue();
-        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
+        mDeviceImpl.onVirtualDisplayRemoved(DISPLAY_ID_1);
         verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt());
     }
 
     @Test
     public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
@@ -825,7 +829,7 @@
 
     @Test
     public void createVirtualTouchscreen_positiveDisplayDimension_successful() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         VirtualTouchscreenConfig positiveConfig =
                 new VirtualTouchscreenConfig.Builder(
                         /* touchscrenWidth= */ 600, /* touchscreenHeight= */ 800)
@@ -863,7 +867,7 @@
 
     @Test
     public void createVirtualNavigationTouchpad_positiveDisplayDimension_successful() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         VirtualNavigationTouchpadConfig positiveConfig =
                 new VirtualNavigationTouchpadConfig.Builder(
                         /* touchpadHeight= */ 50, /* touchpadWidth= */ 50)
@@ -888,7 +892,7 @@
 
     @Test
     public void createVirtualDpad_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
             assertThrows(SecurityException.class,
                     () -> mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER));
@@ -897,7 +901,7 @@
 
     @Test
     public void createVirtualKeyboard_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
             assertThrows(SecurityException.class,
                     () -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER));
@@ -906,7 +910,7 @@
 
     @Test
     public void createVirtualMouse_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
             assertThrows(SecurityException.class,
                     () -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER));
@@ -915,7 +919,7 @@
 
     @Test
     public void createVirtualTouchscreen_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
             assertThrows(SecurityException.class,
                     () -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER));
@@ -924,7 +928,7 @@
 
     @Test
     public void createVirtualNavigationTouchpad_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
             assertThrows(SecurityException.class,
                     () -> mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG,
@@ -934,7 +938,7 @@
 
     @Test
     public void onAudioSessionStarting_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
             assertThrows(SecurityException.class,
                     () -> mDeviceImpl.onAudioSessionStarting(
@@ -951,7 +955,7 @@
 
     @Test
     public void createVirtualDpad_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER);
         assertWithMessage("Virtual dpad should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -961,7 +965,7 @@
 
     @Test
     public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -971,7 +975,7 @@
 
     @Test
     public void createVirtualKeyboard_keyboardCreated_localeUpdated() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches")
                 .that(mInputController.getInputDeviceDescriptors())
@@ -992,7 +996,7 @@
                         .setAssociatedDisplayId(DISPLAY_ID_1)
                         .build();
 
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualKeyboard(configWithoutExplicitLayoutInfo, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches")
                 .that(mInputController.getInputDeviceDescriptors())
@@ -1005,7 +1009,7 @@
 
     @Test
     public void virtualDeviceWithoutKeyboard_noLocaleUpdate() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
 
         // no preceding call to createVirtualKeyboard()
         assertThat(mDeviceImpl.getDeviceLocaleList()).isNull();
@@ -1013,7 +1017,7 @@
 
     @Test
     public void createVirtualMouse_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER);
         assertWithMessage("Virtual mouse should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1023,7 +1027,7 @@
 
     @Test
     public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER);
         assertWithMessage("Virtual touchscreen should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1033,7 +1037,7 @@
 
     @Test
     public void createVirtualNavigationTouchpad_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, BINDER);
         assertWithMessage("Virtual navigation touchpad should register fd when the display matches")
                 .that(
@@ -1055,8 +1059,7 @@
 
     @Test
     public void onAudioSessionStarting_hasVirtualAudioController() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
 
         mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
 
@@ -1065,8 +1068,7 @@
 
     @Test
     public void onAudioSessionEnded_noVirtualAudioController() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
 
         mDeviceImpl.onAudioSessionEnded();
@@ -1076,8 +1078,7 @@
 
     @Test
     public void close_cleanVirtualAudioController() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
 
         mDeviceImpl.close();
@@ -1321,9 +1322,9 @@
 
     @Test
     public void setShowPointerIcon_setsValueForAllDisplays() {
-        mDeviceImpl.mVirtualDisplayIds.add(1);
-        mDeviceImpl.mVirtualDisplayIds.add(2);
-        mDeviceImpl.mVirtualDisplayIds.add(3);
+        addVirtualDisplay(mDeviceImpl, 1);
+        addVirtualDisplay(mDeviceImpl, 2);
+        addVirtualDisplay(mDeviceImpl, 3);
         VirtualMouseConfig config1 = new VirtualMouseConfig.Builder()
                 .setAssociatedDisplayId(1)
                 .setInputDeviceName(DEVICE_NAME_1)
@@ -1346,7 +1347,9 @@
         mDeviceImpl.createVirtualMouse(config1, BINDER);
         mDeviceImpl.createVirtualMouse(config2, BINDER);
         mDeviceImpl.createVirtualMouse(config3, BINDER);
+        clearInvocations(mInputManagerInternalMock);
         mDeviceImpl.setShowPointerIcon(false);
+
         verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt());
         verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt());
         mDeviceImpl.setShowPointerIcon(true);
@@ -1355,9 +1358,8 @@
 
     @Test
     public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -1376,9 +1378,8 @@
 
     @Test
     public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -1397,9 +1398,8 @@
 
     @Test
     public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -1418,9 +1418,8 @@
 
     @Test
     public void openVendingOnVirtualDisplay_startBlockedAlertActivity() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -1439,9 +1438,8 @@
 
     @Test
     public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -1460,9 +1458,8 @@
 
     @Test
     public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() {
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
@@ -1482,9 +1479,8 @@
     @Test
     public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
 
         gwpc.onRunningAppsChanged(uids);
@@ -1497,11 +1493,10 @@
     @Test
     public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
-        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
+        gwpc.unregisterRunningAppsChangedListener(mDeviceImpl);
 
         // This call should not throw any exceptions.
         gwpc.onRunningAppsChanged(uids);
@@ -1512,9 +1507,8 @@
     @Test
     public void canActivityBeLaunched_activityCanLaunch() {
         Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -1537,9 +1531,8 @@
         doReturn(interceptor).when(interceptor).asBinder();
         doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
 
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -1581,9 +1574,8 @@
         doReturn(interceptor).when(interceptor).asBinder();
         doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
 
-        mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
                 NONBLOCKED_APP_PACKAGE_NAME,
@@ -1626,8 +1618,7 @@
     }
 
     @Test
-    public void
-            restrictedActivityOnNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() {
+    public void restrictedActivityNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() {
         Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"), "def");
         verify(mContext).startActivityAsUser(argThat(intent ->
                 intent.filterEquals(blockedAppIntent)), any(), any());
@@ -1654,15 +1645,15 @@
 
     @Test
     public void getDisplayIdsForDevice_oneDisplay_resultContainsCorrectDisplayId() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
         assertThat(displayIds).containsExactly(DISPLAY_ID_1);
     }
 
     @Test
     public void getDisplayIdsForDevice_twoDisplays_resultContainsCorrectDisplayIds() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_2);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_2);
         ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
         assertThat(displayIds).containsExactly(DISPLAY_ID_1, DISPLAY_ID_2);
     }
@@ -1677,15 +1668,22 @@
     private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
             VirtualDeviceParams params) {
         VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
-                mAssociationInfo, new Binder(), ownerUid, virtualDeviceId,
-                mInputController, mSensorController, mCameraAccessController,
-                /* onDeviceCloseListener= */ deviceId -> mVdms.removeVirtualDevice(deviceId),
+                mAssociationInfo, mVdms, new Binder(), ownerUid, virtualDeviceId,
+                mInputController, mSensorController, mCameraAccessController
+                /* onDeviceCloseListener= */ /*deviceId -> mVdms.removeVirtualDevice(deviceId)*/,
                 mPendingTrampolineCallback, mActivityListener, mSoundEffectListener,
-                mRunningAppsChangedCallback, params);
+                mRunningAppsChangedCallback, params, new DisplayManagerGlobal(mIDisplayManager));
         mVdms.addVirtualDevice(virtualDeviceImpl);
         return virtualDeviceImpl;
     }
 
+    private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId) {
+        when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback),
+                eq(virtualDevice), any(), any())).thenReturn(displayId);
+        virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback,
+                NONBLOCKED_APP_PACKAGE_NAME);
+    }
+
     /** Helper class to drop permissions temporarily and restore them at the end of a test. */
     static final class DropShellPermissionsTemporarily implements AutoCloseable {
         DropShellPermissionsTemporarily() {
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java
index fb1a8f8..c0a994b 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java
@@ -40,6 +40,9 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.List;
 
@@ -49,7 +52,8 @@
     private static final int CONTEXT_HUB_ID = 3;
     private static final String CONTEXT_HUB_STRING = "Context Hub Info Test";
 
-    private Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
     @Mock private IContextHubWrapper mMockContextHubWrapper;
     @Mock private ContextHubInfo mMockContextHubInfo;
     @Rule public final MockitoRule mockito = MockitoJUnit.rule();
@@ -62,20 +66,29 @@
         when(mMockContextHubInfo.toString()).thenReturn(CONTEXT_HUB_STRING);
         when(mMockContextHubWrapper.getHubs()).thenReturn(hubInfo);
 
-        when(mMockContextHubWrapper.supportsLocationSettingNotifications())
-                .thenReturn(true);
+        when(mMockContextHubWrapper.supportsLocationSettingNotifications()).thenReturn(true);
         when(mMockContextHubWrapper.supportsWifiSettingNotifications()).thenReturn(true);
-        when(mMockContextHubWrapper.supportsAirplaneModeSettingNotifications())
-                .thenReturn(true);
-        when(mMockContextHubWrapper.supportsMicrophoneSettingNotifications())
-                .thenReturn(true);
+        when(mMockContextHubWrapper.supportsAirplaneModeSettingNotifications()).thenReturn(true);
+        when(mMockContextHubWrapper.supportsMicrophoneSettingNotifications()).thenReturn(true);
         when(mMockContextHubWrapper.supportsBtSettingNotifications()).thenReturn(true);
     }
 
-// TODO (b/254290317): These existing tests are to setup the testing infra for the ContextHub
-//                     service and verify the constructor correctly registers a context hub.
-//                     We need to augment these tests to cover the full behavior of the
-//                     ContextHub service
+    @Test
+    public void testDump_emptyPreloadedNanoappList() {
+        when(mMockContextHubWrapper.getPreloadedNanoappIds()).thenReturn(null);
+        StringWriter stringWriter = new StringWriter();
+
+        ContextHubService service = new ContextHubService(mContext, mMockContextHubWrapper);
+        service.dump(
+                new FileDescriptor(), new PrintWriter(stringWriter), /* args= */ new String[0]);
+
+        assertThat(stringWriter.toString()).isNotEmpty();
+    }
+
+    // TODO (b/254290317): These existing tests are to setup the testing infra for the ContextHub
+    //                     service and verify the constructor correctly registers a context hub.
+    //                     We need to augment these tests to cover the full behavior of the
+    //                     ContextHub service
 
     @Test
     public void testConstructorRegistersContextHub() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 9fc46c5..4a2e5d7 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -86,6 +86,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
+import android.util.Pair;
 import android.util.Range;
 
 import com.android.internal.app.ChooserActivity;
@@ -186,6 +187,7 @@
     private ShortcutInfo mShortcutInfo;
     private TestInjector mInjector;
     private TestLooper mLooper;
+    private TestPerPackageThrottler mShortcutThrottler;
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
@@ -275,7 +277,9 @@
 
         mInjector = new TestInjector();
         mLooper = new TestLooper();
-        mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
+        mShortcutThrottler = new TestPerPackageThrottler();
+        mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper(),
+                mShortcutThrottler);
         mDataManager.initialize();
 
         when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
@@ -283,10 +287,7 @@
 
         mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        when(mShortcutServiceInternal.getShortcuts(
-                anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
-                anyInt(), anyInt(), anyInt(), anyInt()))
-                .thenReturn(Collections.singletonList(mShortcutInfo));
+        mockGetShortcuts(Collections.singletonList(mShortcutInfo));
         verify(mShortcutServiceInternal).addShortcutChangeCallback(
                 mShortcutChangeCallbackCaptor.capture());
         mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
@@ -972,6 +973,7 @@
                 buildPerson());
         ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3",
                 buildPerson());
+        mockGetShortcuts(List.of(shortcut1, shortcut2, shortcut3));
         mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
                 Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY));
         mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
@@ -1223,7 +1225,6 @@
                     eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
         }
     }
-
     @Test
     public void testUncacheOldestCachedShortcut_missingNotificationEvents() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1233,6 +1234,7 @@
             ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
                     buildPerson());
             shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+            mockGetShortcuts(Collections.singletonList(shortcut));
             mShortcutChangeCallback.onShortcutsAddedOrUpdated(
                     TEST_PKG_NAME,
                     Collections.singletonList(shortcut),
@@ -1252,7 +1254,6 @@
                     eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
         }
     }
-
     @Test
     public void testUncacheOldestCachedShortcut_legacyConversation() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1274,6 +1275,7 @@
             ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
                     buildPerson());
             shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+            mockGetShortcuts(Collections.singletonList(shortcut));
             mShortcutChangeCallback.onShortcutsAddedOrUpdated(
                     TEST_PKG_NAME,
                     Collections.singletonList(shortcut),
@@ -1311,7 +1313,8 @@
         mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
         byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
 
-        DataManager dataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
+        DataManager dataManager = new DataManager(
+                mContext, mInjector, mLooper.getLooper(), mShortcutThrottler);
         dataManager.onUserUnlocked(USER_ID_PRIMARY);
         dataManager.restore(USER_ID_PRIMARY, payload);
         ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
@@ -1723,6 +1726,13 @@
         return (queryFlags & flag) != 0;
     }
 
+    private void mockGetShortcuts(List<ShortcutInfo> shortcutInfoList) {
+        when(mShortcutServiceInternal.getShortcuts(
+                anyInt(), anyString(), anyLong(), anyString(), any(), any(), any(),
+                anyInt(), anyInt(), anyInt(), anyInt()))
+                .thenReturn(shortcutInfoList);
+    }
+
     // "Sends" a notification to a non-customized notification channel - the notification channel
     // is something generic like "messages" and the notification has a  shortcut id
     private void sendGenericNotification() {
@@ -1984,4 +1994,11 @@
             return mUsageStatsQueryHelper;
         }
     }
+
+    private static class TestPerPackageThrottler implements PerPackageThrottler {
+        @Override
+        public void scheduleDebounced(Pair<String, Integer> pkgUserId, Runnable runnable) {
+            runnable.run();
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PerPackageThrottlerImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/PerPackageThrottlerImplTest.java
new file mode 100644
index 0000000..672cbb9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/PerPackageThrottlerImplTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.util.Pair;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(JUnit4.class)
+public class PerPackageThrottlerImplTest {
+    private static final int DEBOUNCE_INTERVAL = 500;
+    private static final String PKG_ONE = "pkg_one";
+    private static final String PKG_TWO = "pkg_two";
+    private static final int USER_ID = 10;
+
+    private final OffsettableClock mClock = new OffsettableClock.Stopped();
+    private final TestHandler mTestHandler = new TestHandler(null, mClock);
+    private PerPackageThrottlerImpl mThrottler;
+
+    @Before
+    public void setUp() {
+        mThrottler = new PerPackageThrottlerImpl(mTestHandler, DEBOUNCE_INTERVAL);
+    }
+
+    @Test
+    public void scheduleDebounced() {
+        AtomicBoolean pkgOneRan = new AtomicBoolean();
+        AtomicBoolean pkgTwoRan = new AtomicBoolean();
+
+        mThrottler.scheduleDebounced(new Pair<>(PKG_ONE, USER_ID), () -> pkgOneRan.set(true));
+        mThrottler.scheduleDebounced(new Pair<>(PKG_ONE, USER_ID), () -> pkgOneRan.set(true));
+        mThrottler.scheduleDebounced(new Pair<>(PKG_TWO, USER_ID), () -> pkgTwoRan.set(true));
+        mThrottler.scheduleDebounced(new Pair<>(PKG_TWO, USER_ID), () -> pkgTwoRan.set(true));
+
+        assertFalse(pkgOneRan.get());
+        assertFalse(pkgTwoRan.get());
+        mClock.fastForward(DEBOUNCE_INTERVAL);
+        mTestHandler.timeAdvance();
+        assertTrue(pkgOneRan.get());
+        assertTrue(pkgTwoRan.get());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 5a0867f..daa6823 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -386,16 +386,16 @@
                 .when(mMockContext).enforceCallingPermission(anyString(), any());
 
         assertThrows(SecurityException.class,
-                () -> mTimeDetectorService.clearNetworkTime());
+                () -> mTimeDetectorService.clearLatestNetworkTime());
         verify(mMockContext).enforceCallingPermission(
                 eq(android.Manifest.permission.SET_TIME), anyString());
     }
 
     @Test
-    public void testClearNetworkTime() throws Exception {
+    public void testClearLatestNetworkSuggestion() throws Exception {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
-        mTimeDetectorService.clearNetworkTime();
+        mTimeDetectorService.clearLatestNetworkTime();
 
         verify(mMockContext).enforceCallingPermission(
                 eq(android.Manifest.permission.SET_TIME), anyString());
@@ -403,53 +403,48 @@
     }
 
     @Test
-    public void testLatestNetworkTime() {
-        NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
-                1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
-        when(mMockNtpTrustedTime.getCachedTimeResult())
-                .thenReturn(latestNetworkTime);
-        UnixEpochTime expected = new UnixEpochTime(
-                latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
-        assertEquals(expected, mTimeDetectorService.latestNetworkTime());
-    }
-
-    @Test
-    public void testLatestNetworkTime_noTimeAvailable() {
-        when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
-        assertThrows(ParcelableException.class, () -> mTimeDetectorService.latestNetworkTime());
-    }
-
-    @Test
     public void testGetLatestNetworkSuggestion() {
-        if (TimeDetectorNetworkTimeHelper.isInUse()) {
-            NetworkTimeSuggestion latestNetworkTime = createNetworkTimeSuggestion();
-            mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkTime);
+        NetworkTimeSuggestion latestNetworkSuggestion = createNetworkTimeSuggestion();
+        mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkSuggestion);
 
-            assertEquals(latestNetworkTime, mTimeDetectorService.getLatestNetworkSuggestion());
+        assertEquals(latestNetworkSuggestion, mTimeDetectorService.getLatestNetworkSuggestion());
+    }
+
+    @Test
+    public void testGetLatestNetworkSuggestion_noTimeAvailable() {
+        mFakeTimeDetectorStrategySpy.setLatestNetworkTime(null);
+
+        assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
+    }
+
+    @Test
+    public void testLatestNetworkTime() {
+        if (TimeDetectorNetworkTimeHelper.isInUse()) {
+            NetworkTimeSuggestion latestNetworkSuggestion = createNetworkTimeSuggestion();
+            mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkSuggestion);
+
+            assertEquals(latestNetworkSuggestion.getUnixEpochTime(),
+                    mTimeDetectorService.latestNetworkTime());
         } else {
             NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
                     1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
             when(mMockNtpTrustedTime.getCachedTimeResult())
                     .thenReturn(latestNetworkTime);
-            UnixEpochTime expectedUnixEpochTime = new UnixEpochTime(
+            UnixEpochTime expected = new UnixEpochTime(
                     latestNetworkTime.getElapsedRealtimeMillis(),
                     latestNetworkTime.getTimeMillis());
-            NetworkTimeSuggestion expected = new NetworkTimeSuggestion(
-                    expectedUnixEpochTime, latestNetworkTime.getUncertaintyMillis());
-            assertEquals(expected, mTimeDetectorService.getLatestNetworkSuggestion());
+            assertEquals(expected, mTimeDetectorService.latestNetworkTime());
         }
     }
 
     @Test
-    public void testGetLatestNetworkSuggestion_noTimeAvailable() {
+    public void testLatestNetworkTime_noTimeAvailable() {
         if (TimeDetectorNetworkTimeHelper.isInUse()) {
             mFakeTimeDetectorStrategySpy.setLatestNetworkTime(null);
-
-            assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
         } else {
             when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
-            assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
         }
+        assertThrows(ParcelableException.class, () -> mTimeDetectorService.latestNetworkTime());
     }
 
     @Test
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index b921838..4c0361d 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -263,7 +263,7 @@
                 + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
                 + packageName + " 0");
         waitUntil("Failed to get shortcut access",
-                () -> hasShortcutAccess(instrumentation, packageName), 20);
+                () -> hasShortcutAccess(instrumentation, packageName), 60);
     }
 
     public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index ff5ede7..56461f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -17,8 +17,11 @@
 package com.android.server.wm;
 
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.window.BackNavigationInfo.typeToString;
 
@@ -42,8 +45,10 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.os.Bundle;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 import android.view.WindowManager;
 import android.window.BackAnimationAdapter;
 import android.window.BackMotionEvent;
@@ -320,6 +325,64 @@
         assertThat(backNavigationInfo).isNull();
     }
 
+    @Test
+    public void testTransitionHappensCancelNavigation() {
+        // Create a floating task and a fullscreen task, then navigating on fullscreen task.
+        // The navigation should not been cancelled when transition happens on floating task, and
+        // only be cancelled when transition happens on the navigating task.
+        final Task floatingTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
+                ACTIVITY_TYPE_STANDARD);
+        final ActivityRecord baseFloatingActivity = createActivityRecord(floatingTask);
+
+        final Task fullscreenTask = createTopTaskWithActivity();
+        withSystemCallback(fullscreenTask);
+        final ActivityRecord baseFullscreenActivity = fullscreenTask.getTopMostActivity();
+
+        final CountDownLatch navigationObserver = new CountDownLatch(1);
+        startBackNavigation(navigationObserver);
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        final ActivityRecord secondFloatingActivity = createActivityRecord(floatingTask);
+        opening.add(secondFloatingActivity);
+        closing.add(baseFloatingActivity);
+        mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
+        assertEquals("Transition happen on an irrelevant task, callback should not been called",
+                1, navigationObserver.getCount());
+
+        // Create a new activity above navigation target, the transition should cancel navigation.
+        final ActivityRecord topFullscreenActivity = createActivityRecord(fullscreenTask);
+        opening.clear();
+        closing.clear();
+        opening.add(topFullscreenActivity);
+        closing.add(baseFullscreenActivity);
+        mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
+        assertEquals("Transition happen on navigation task, callback should have been called",
+                0, navigationObserver.getCount());
+    }
+
+    @Test
+    public void testWindowFocusChangeCancelNavigation() {
+        Task task = createTopTaskWithActivity();
+        withSystemCallback(task);
+        WindowState focusWindow = task.getTopVisibleAppMainWindow();
+        final CountDownLatch navigationObserver = new CountDownLatch(1);
+        startBackNavigation(navigationObserver);
+
+        mBackNavigationController.onFocusChanged(null);
+        assertEquals("change focus to null, callback should not have been called",
+                1, navigationObserver.getCount());
+        mBackNavigationController.onFocusChanged(focusWindow);
+        assertEquals("change focus back, callback should not have been called",
+                1, navigationObserver.getCount());
+
+        WindowState newWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlayWindow");
+        addToWindowMap(newWindow, true);
+        mBackNavigationController.onFocusChanged(newWindow);
+        assertEquals("Focus change, callback should have been called",
+                0, navigationObserver.getCount());
+    }
+
     private IOnBackInvokedCallback withSystemCallback(Task task) {
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
         task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
@@ -336,7 +399,14 @@
 
     @Nullable
     private BackNavigationInfo startBackNavigation() {
-        return mBackNavigationController.startBackNavigation(null, mBackAnimationAdapter);
+        return mBackNavigationController.startBackNavigation(
+                createNavigationObserver(null), mBackAnimationAdapter);
+    }
+
+    @Nullable
+    private BackNavigationInfo startBackNavigation(CountDownLatch navigationObserverLatch) {
+        return mBackNavigationController.startBackNavigation(
+                createNavigationObserver(navigationObserverLatch), mBackAnimationAdapter);
     }
 
     @NonNull
@@ -371,6 +441,14 @@
         };
     }
 
+    private RemoteCallback createNavigationObserver(CountDownLatch latch) {
+        return new RemoteCallback(result -> {
+            if (latch != null) {
+                latch.countDown();
+            }
+        });
+    }
+
     private Task initHomeActivity() {
         final Task task = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
         task.forAllLeafTasks((t) -> {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 4359234..025e1dc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -30,7 +30,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Disabled;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.content.Context;
@@ -41,7 +41,6 @@
 import android.media.AudioManagerInternal;
 import android.media.permission.Identity;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
@@ -89,8 +88,7 @@
     static final boolean DEBUG = false;
 
     /**
-     * For apps targeting Android API Build.VERSION_CODES#UPSIDE_DOWN_CAKE and above,
-     * implementors of the HotwordDetectionService must not augment the phrase IDs which are
+     * Implementors of the HotwordDetectionService must not augment the phrase IDs which are
      * supplied via HotwordDetectionService
      * #onDetect(AlwaysOnHotwordDetector.EventPayload, long, HotwordDetectionService.Callback).
      *
@@ -104,7 +102,7 @@
      * </p>
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Disabled
     public static final long ENFORCE_HOTWORD_PHRASE_ID = 215066299L;
 
     private static final String KEY_RESTART_PERIOD_IN_SECONDS = "restart_period_in_seconds";
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index 6b2bea0..97538c1 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -76,7 +76,10 @@
     }
 
     /**
-     * Request Telecom set the call state to active.
+     * Request Telecom set the call state to active. This method should be called when either an
+     * outgoing call is ready to go active or a held call is ready to go active again. For incoming
+     * calls that are ready to be answered, use
+     * {@link CallControl#answer(int, Executor, OutcomeReceiver)}.
      *
      * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
      *                 will be called on.
@@ -106,6 +109,43 @@
     }
 
     /**
+     * Request Telecom answer an incoming call.  For outgoing calls and calls that have been placed
+     * on hold, use {@link CallControl#setActive(Executor, OutcomeReceiver)}.
+     *
+     * @param videoState to report to Telecom. Telecom will store VideoState in the event another
+     *                   service/device requests it in order to continue the call on another screen.
+     * @param executor   The {@link Executor} on which the {@link OutcomeReceiver} callback
+     *                   will be called on.
+     * @param callback   that will be completed on the Telecom side that details success or failure
+     *                   of the requested operation.
+     *
+     *                   {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+     *                   switched the call state to active
+     *
+     *                   {@link OutcomeReceiver#onError} will be called if Telecom has failed to set
+     *                   the call state to active.  A {@link CallException} will be passed
+     *                   that details why the operation failed.
+     */
+    public void answer(@android.telecom.CallAttributes.CallType int videoState,
+            @CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Void, CallException> callback) {
+        validateVideoState(videoState);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        if (mServerInterface != null) {
+            try {
+                mServerInterface.answer(videoState, mCallId,
+                        new CallControlResultReceiver("answer", executor, callback));
+
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        } else {
+            throw new IllegalStateException(INTERFACE_ERROR_MSG);
+        }
+    }
+
+    /**
      * Request Telecom set the call state to inactive. This the same as hold for two call endpoints
      * but can be extended to setting a meeting to inactive.
      *
@@ -343,4 +383,13 @@
         }
     }
 
+    /** @hide */
+    private void validateVideoState(@android.telecom.CallAttributes.CallType int videoState) {
+        if (videoState != CallAttributes.AUDIO_CALL && videoState != CallAttributes.VIDEO_CALL) {
+            throw new IllegalArgumentException(TextUtils.formatSimple(
+                    "The VideoState argument passed in, %d , is not a valid VideoState. The "
+                            + "VideoState choices are limited to CallAttributes.AUDIO_CALL or"
+                            + "CallAttributes.VIDEO_CALL", videoState));
+        }
+    }
 }
diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
index 3e651e9..5e2c923 100644
--- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
@@ -27,6 +27,7 @@
  */
 oneway interface ICallControl {
     void setActive(String callId, in ResultReceiver callback);
+    void answer(int videoState, String callId, in ResultReceiver callback);
     void setInactive(String callId, in ResultReceiver callback);
     void disconnect(String callId, in DisconnectCause disconnectCause, in ResultReceiver callback);
     void startCallStreaming(String callId, in ResultReceiver callback);
diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java
index 7eccd1a..728dfa6 100644
--- a/telephony/java/android/service/euicc/EuiccProfileInfo.java
+++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.SubscriptionInfo;
 import android.telephony.UiccAccessRule;
 import android.text.TextUtils;
 
@@ -451,6 +452,8 @@
                 + mPolicyRules
                 + ", accessRules="
                 + Arrays.toString(mAccessRules)
+                + ", iccid="
+                + SubscriptionInfo.givePrintableIccid(mIccid)
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b418a02..3e2c7c4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -4401,7 +4401,6 @@
      *
      * @throws IllegalArgumentException if subscription is invalid.
      * @throws SecurityException if the caller doesn't have permissions required.
-     * @throws IllegalStateException if subscription service is not available.
      *
      * @hide
      */
@@ -4418,8 +4417,7 @@
             if (iSub != null) {
                 return iSub.getSubscriptionUserHandle(subscriptionId);
             } else {
-                throw new IllegalStateException("[getSubscriptionUserHandle]: "
-                        + "subscription service unavailable");
+                Log.e(LOG_TAG, "[getSubscriptionUserHandle]: subscription service unavailable");
             }
         } catch (RemoteException ex) {
             ex.rethrowAsRuntimeException();
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 174675f..a8fb36b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -342,8 +342,8 @@
     /**
      * @return The SIM slot index associated with this ImsFeature.
      *
-     * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the
-     * subscription IDs associated with this slot.
+     * @see SubscriptionManager#getSubscriptionId(int) for more information on getting the
+     * subscription ID associated with this slot.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteService.java b/telephony/java/android/telephony/satellite/stub/SatelliteService.java
index 3861c5b..5b96e34 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteService.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteService.java
@@ -20,7 +20,8 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
-import android.util.Log;
+
+import com.android.telephony.Rlog;
 
 /**
  * Main SatelliteService implementation, which binds via the Telephony SatelliteServiceController.
@@ -44,7 +45,7 @@
  * @hide
  */
 public class SatelliteService extends Service {
-    public static final String TAG = "SatelliteService";
+    private static final String TAG = "SatelliteService";
 
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.telephony.satellite.SatelliteService";
@@ -55,7 +56,7 @@
     @Override
     public IBinder onBind(Intent intent) {
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            Log.e(TAG, "SatelliteService bound");
+            Rlog.d(TAG, "SatelliteService bound");
             return new SatelliteImplBase(Runnable::run).getBinder();
         }
         return null;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index 08786d3..fb6fb22 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -19,12 +19,15 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
+import android.platform.test.rule.SettingOverrideRule
+import android.provider.Settings
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerBuilder
 import com.android.server.wm.flicker.FlickerTest
 import com.android.server.wm.flicker.FlickerTestFactory
 import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
 import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
+import org.junit.ClassRule
 import org.junit.FixMethodOrder
 import org.junit.Ignore
 import org.junit.Test
@@ -130,5 +133,16 @@
         fun getParams(): Collection<FlickerTest> {
             return FlickerTestFactory.nonRotationTests()
         }
+
+        /**
+         * Ensures that posted notifications will be visible on the lockscreen and not
+         * suppressed due to being marked as seen.
+         */
+        @ClassRule
+        @JvmField
+        val disableUnseenNotifFilterRule = SettingOverrideRule(
+            Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+            /* value= */ "0",
+        )
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index a5d85cc..32276d6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -18,6 +18,8 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
+import android.platform.test.rule.SettingOverrideRule
+import android.provider.Settings
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerBuilder
 import com.android.server.wm.flicker.FlickerTest
@@ -25,6 +27,7 @@
 import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
 import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
+import org.junit.ClassRule
 import org.junit.FixMethodOrder
 import org.junit.Ignore
 import org.junit.Test
@@ -145,5 +148,16 @@
         fun getParams(): Collection<FlickerTest> {
             return FlickerTestFactory.nonRotationTests()
         }
+
+        /**
+         * Ensures that posted notifications will be visible on the lockscreen and not
+         * suppressed due to being marked as seen.
+         */
+        @ClassRule
+        @JvmField
+        val disableUnseenNotifFilterRule = SettingOverrideRule(
+            Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+            /* value= */ "0",
+        )
     }
 }
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index ff7246f..b751f1e 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -130,10 +130,17 @@
 
             @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
                     android.Manifest.permission.NETWORK_SETUP_WIZARD})
+            /**
+             * checkPermissions is using checkCallingOrSelfPermission to support CTS testing of this
+             * service. This does allow a process to bind to itself if it holds the proper
+             * permission. We do not consider this to be an issue given that the process can already
+             * access the service data since they are in the same process.
+             */
             private void checkPermissions() {
-                if (checkCallingPermission(NETWORK_SETTINGS) != PackageManager.PERMISSION_GRANTED
-                        && checkCallingPermission(NETWORK_SETUP_WIZARD)
-                                != PackageManager.PERMISSION_GRANTED) {
+                if (checkCallingOrSelfPermission(NETWORK_SETTINGS)
+                        != PackageManager.PERMISSION_GRANTED
+                        && checkCallingOrSelfPermission(NETWORK_SETUP_WIZARD)
+                        != PackageManager.PERMISSION_GRANTED) {
                     throw new SecurityException("Calling process must have NETWORK_SETTINGS or"
                             + " NETWORK_SETUP_WIZARD permission");
                 }