Merge "Revert "More robust updating of "runnable" list."" into udc-dev
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index c967e51..45c6b8c 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -31,6 +31,9 @@
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
-    data: ["trace_configs/*"],
+    data: [
+        ":MultiUserPerfDummyApp",
+        "trace_configs/*",
+    ],
     certificate: "platform",
 }
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index 81cec91..e84aea1 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -33,7 +33,10 @@
 
     test_suites: ["device-tests"],
 
-    data: [":perfetto_artifacts"],
+    data: [
+        ":QueriesAll0",
+        ":perfetto_artifacts",
+    ],
 
     certificate: "platform",
 
diff --git a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
index 0b35101..cbd602f 100644
--- a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
+++ b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
@@ -45,11 +45,13 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.adservices.data.adselection.CustomAudienceSignals;
-import com.android.adservices.service.adselection.AdDataArgument;
-import com.android.adservices.service.adselection.AdSelectionConfigArgument;
-import com.android.adservices.service.adselection.AdWithBidArgument;
-import com.android.adservices.service.adselection.CustomAudienceBiddingSignalsArgument;
-import com.android.adservices.service.adselection.CustomAudienceScoringSignalsArgument;
+import com.android.adservices.service.adselection.AdCounterKeyCopier;
+import com.android.adservices.service.adselection.AdCounterKeyCopierNoOpImpl;
+import com.android.adservices.service.adselection.AdDataArgumentUtil;
+import com.android.adservices.service.adselection.AdSelectionConfigArgumentUtil;
+import com.android.adservices.service.adselection.AdWithBidArgumentUtil;
+import com.android.adservices.service.adselection.CustomAudienceBiddingSignalsArgumentUtil;
+import com.android.adservices.service.adselection.CustomAudienceScoringSignalsArgumentUtil;
 import com.android.adservices.service.js.IsolateSettings;
 import com.android.adservices.service.js.JSScriptArgument;
 import com.android.adservices.service.js.JSScriptArrayArgument;
@@ -106,6 +108,14 @@
     private static final Instant ACTIVATION_TIME = CLOCK.instant();
     private static final Instant EXPIRATION_TIME = CLOCK.instant().plus(Duration.ofDays(1));
     private static final AdSelectionSignals CONTEXTUAL_SIGNALS = AdSelectionSignals.EMPTY;
+    private static final AdCounterKeyCopier AD_COUNTER_KEY_COPIER_NO_OP =
+            new AdCounterKeyCopierNoOpImpl();
+
+    private final AdDataArgumentUtil mAdDataArgumentUtil =
+            new AdDataArgumentUtil(AD_COUNTER_KEY_COPIER_NO_OP);
+    private final AdWithBidArgumentUtil mAdWithBidArgumentUtil =
+            new AdWithBidArgumentUtil(mAdDataArgumentUtil);
+
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
@@ -437,7 +447,7 @@
         List<AdData> adDataList = getSampleAdDataList(numOfAds, "https://ads.example/");
         ImmutableList.Builder<JSScriptArgument> adDataListArgument = new ImmutableList.Builder<>();
         for (AdData adData : adDataList) {
-            adDataListArgument.add(AdDataArgument.asScriptArgument("ignored", adData));
+            adDataListArgument.add(mAdDataArgumentUtil.asScriptArgument("ignored", adData));
         }
         AdSelectionSignals perBuyerSignals = generatePerBuyerSignals(numOfAds);
         AdSelectionSignals auctionSignals = AdSelectionSignals.fromString("{\"auctionSignal1"
@@ -455,7 +465,7 @@
                 .add(jsonArg("perBuyerSignals", perBuyerSignals))
                 .add(jsonArg("trustedBiddingSignals", trustedBiddingSignals))
                 .add(jsonArg("contextualSignals", CONTEXTUAL_SIGNALS))
-                .add(CustomAudienceBiddingSignalsArgument.asScriptArgument(
+                .add(CustomAudienceBiddingSignalsArgumentUtil.asScriptArgument(
                         "customAudienceBiddingSignal", customAudienceSignals))
                 .build();
         InputStream testJsInputStream = sContext.getAssets().open(
@@ -485,7 +495,8 @@
         ImmutableList.Builder<JSScriptArgument> adWithBidArrayArgument =
                 new ImmutableList.Builder<>();
         for (AdWithBid adWithBid : adWithBidList) {
-            adWithBidArrayArgument.add(AdWithBidArgument.asScriptArgument("adWithBid", adWithBid));
+            adWithBidArrayArgument.add(
+                    mAdWithBidArgumentUtil.asScriptArgument("adWithBid", adWithBid));
         }
         AdTechIdentifier seller = AdTechIdentifier.fromString("www.example-ssp.com");
         AdSelectionSignals sellerSignals = AdSelectionSignals.fromString("{\"signals\":[]}");
@@ -507,12 +518,12 @@
 
         ImmutableList<JSScriptArgument> args = ImmutableList.<JSScriptArgument>builder()
                 .add(arrayArg("adsWithBids", adWithBidArrayArgument.build()))
-                .add(AdSelectionConfigArgument.asScriptArgument(adSelectionConfig,
+                .add(AdSelectionConfigArgumentUtil.asScriptArgument(adSelectionConfig,
                         "adSelectionConfig"))
                 .add(jsonArg("sellerSignals", sellerSignals))
                 .add(jsonArg("trustedScoringSignals", trustedScoringSignalsJson))
                 .add(jsonArg("contextualSignals", CONTEXTUAL_SIGNALS))
-                .add(CustomAudienceScoringSignalsArgument.asScriptArgument(
+                .add(CustomAudienceScoringSignalsArgumentUtil.asScriptArgument(
                         "customAudienceScoringSignal", customAudienceSignals))
                 .build();
         InputStream testJsInputStream = sContext.getAssets().open(
diff --git a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
index 776d913..3cfddc6 100644
--- a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
+++ b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
@@ -65,8 +65,12 @@
     @NonNull
     @Override
     public JobScheduler forNamespace(@NonNull String namespace) {
+        namespace = sanitizeNamespace(namespace);
         if (namespace == null) {
-            throw new IllegalArgumentException("namespace cannot be null");
+            throw new NullPointerException("namespace cannot be null");
+        }
+        if (namespace.isEmpty()) {
+            throw new IllegalArgumentException("namespace cannot be empty");
         }
         return new JobSchedulerImpl(this, namespace);
     }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index b8847ad..d59d430 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -270,6 +270,9 @@
      * otherwise. Attempting to update a job scheduled in another namespace will not be possible
      * but will instead create or update the job inside the current namespace. A JobScheduler
      * instance dedicated to a namespace must be used to schedule or update jobs in that namespace.
+     *
+     * <p class="note">Since leading and trailing whitespace can lead to hard-to-debug issues,
+     * they will be {@link String#trim() trimmed}. An empty String (after trimming) is not allowed.
      * @see #getNamespace()
      */
     @NonNull
@@ -287,6 +290,15 @@
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
+    /** @hide */
+    @Nullable
+    public static String sanitizeNamespace(@Nullable String namespace) {
+        if (namespace == null) {
+            return null;
+        }
+        return namespace.trim().intern();
+    }
+
     /**
      * Schedule a job to be executed.  Will replace any currently scheduled job with the same
      * ID with the new information in the {@link JobInfo}.  If a job with the given ID is currently
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 3fc87d3..ce381b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -22,7 +22,7 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.PackageOps;
 import android.app.IActivityManager;
-import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -716,11 +716,7 @@
         return true;
     }
 
-    private final class UidObserver extends IUidObserver.Stub {
-        @Override
-        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
-        }
-
+    private final class UidObserver extends android.app.UidObserver {
         @Override
         public void onUidActive(int uid) {
             mHandler.onUidActive(uid);
@@ -740,10 +736,6 @@
         public void onUidCachedChanged(int uid, boolean cached) {
             mHandler.onUidCachedChanged(uid, cached);
         }
-
-        @Override
-        public void onUidProcAdjChanged(int uid) {
-        }
     }
 
     private final class AppOpsWatcher extends IAppOpsCallback.Stub {
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 3fe83a6..887ee5f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -233,7 +233,7 @@
         }
     }
 
-    @VisibleForTesting
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) {
         @Override
         public long millis() {
@@ -241,7 +241,6 @@
         }
     };
 
-    @VisibleForTesting
     public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
         @Override
         public long millis() {
@@ -1552,16 +1551,21 @@
                     jobStatus.getNumPreviousAttempts(),
                     jobStatus.getJob().getMaxExecutionDelayMillis(),
                     /* isDeadlineConstraintSatisfied */ false,
-                    /* isCharging */ false,
-                    /* batteryNotLow */ false,
-                    /* storageNotLow */false,
+                    /* isChargingSatisfied */ false,
+                    /* batteryNotLowSatisfied */ false,
+                    /* storageNotLowSatisfied */false,
                     /* timingDelayConstraintSatisfied */ false,
-                    /* isDeviceIdle */ false,
+                    /* isDeviceIdleSatisfied */ false,
                     /* hasConnectivityConstraintSatisfied */ false,
                     /* hasContentTriggerConstraintSatisfied */ false,
-                    0,
+                    /* jobStartLatencyMs */ 0,
                     jobStatus.getJob().isUserInitiated(),
-                    /* isRunningAsUserInitiatedJob */ false);
+                    /* isRunningAsUserInitiatedJob */ false,
+                    jobStatus.getJob().isPeriodic(),
+                    jobStatus.getJob().getMinLatencyMillis(),
+                    jobStatus.getEstimatedNetworkDownloadBytes(),
+                    jobStatus.getEstimatedNetworkUploadBytes(),
+                    jobStatus.getWorkCount());
 
             // 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
@@ -1982,9 +1986,14 @@
                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
-                    0,
+                    /* jobStartLatencyMs */ 0,
                     cancelled.getJob().isUserInitiated(),
-                    /* isRunningAsUserInitiatedJob */ false);
+                    /* isRunningAsUserInitiatedJob */ false,
+                    cancelled.getJob().isPeriodic(),
+                    cancelled.getJob().getMinLatencyMillis(),
+                    cancelled.getEstimatedNetworkDownloadBytes(),
+                    cancelled.getEstimatedNetworkUploadBytes(),
+                    cancelled.getWorkCount());
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
@@ -4024,6 +4033,18 @@
             return JobScheduler.RESULT_SUCCESS;
         }
 
+        /** Returns a sanitized namespace if valid, or throws an exception if not. */
+        private String validateNamespace(@Nullable String namespace) {
+            namespace = JobScheduler.sanitizeNamespace(namespace);
+            if (namespace != null) {
+                if (namespace.isEmpty()) {
+                    throw new IllegalArgumentException("namespace cannot be empty");
+                }
+                namespace = namespace.intern();
+            }
+            return namespace;
+        }
+
         private int validateRunUserInitiatedJobsPermission(int uid, String packageName) {
             final int state = getRunUserInitiatedJobsPermissionState(uid, packageName);
             if (state == PermissionChecker.PERMISSION_HARD_DENIED) {
@@ -4071,9 +4092,7 @@
                 return result;
             }
 
-            if (namespace != null) {
-                namespace = namespace.intern();
-            }
+            namespace = validateNamespace(namespace);
 
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -4104,9 +4123,7 @@
                 return result;
             }
 
-            if (namespace != null) {
-                namespace = namespace.intern();
-            }
+            namespace = validateNamespace(namespace);
 
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -4145,9 +4162,7 @@
                 return result;
             }
 
-            if (namespace != null) {
-                namespace = namespace.intern();
-            }
+            namespace = validateNamespace(namespace);
 
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -4184,7 +4199,8 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 return new ParceledListSlice<>(
-                        JobSchedulerService.this.getPendingJobsInNamespace(uid, namespace));
+                        JobSchedulerService.this.getPendingJobsInNamespace(uid,
+                                validateNamespace(namespace)));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4196,7 +4212,8 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                return JobSchedulerService.this.getPendingJob(uid, namespace, jobId);
+                return JobSchedulerService.this.getPendingJob(
+                        uid, validateNamespace(namespace), jobId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4208,7 +4225,8 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                return JobSchedulerService.this.getPendingJobReason(uid, namespace, jobId);
+                return JobSchedulerService.this.getPendingJobReason(
+                        uid, validateNamespace(namespace), jobId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4238,7 +4256,7 @@
                 JobSchedulerService.this.cancelJobsForUid(uid,
                         // Documentation says only jobs scheduled BY the app will be cancelled
                         /* includeSourceApp */ false,
-                        /* namespaceOnly */ true, namespace,
+                        /* namespaceOnly */ true, validateNamespace(namespace),
                         JobParameters.STOP_REASON_CANCELLED_BY_APP,
                         JobParameters.INTERNAL_STOP_REASON_CANCELED,
                         "cancelAllInNamespace() called by app, callingUid=" + uid);
@@ -4253,7 +4271,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                JobSchedulerService.this.cancelJob(uid, namespace, jobId, uid,
+                JobSchedulerService.this.cancelJob(uid, validateNamespace(namespace), jobId, uid,
                         JobParameters.STOP_REASON_CANCELLED_BY_APP);
             } finally {
                 Binder.restoreCallingIdentity(ident);
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 8355e9c..44700c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -471,7 +471,12 @@
                     job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
                     mExecutionStartTimeElapsed - job.enqueueTime,
                     job.getJob().isUserInitiated(),
-                    job.shouldTreatAsUserInitiatedJob());
+                    job.shouldTreatAsUserInitiatedJob(),
+                    job.getJob().isPeriodic(),
+                    job.getJob().getMinLatencyMillis(),
+                    job.getEstimatedNetworkDownloadBytes(),
+                    job.getEstimatedNetworkUploadBytes(),
+                    job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
                 final String componentPackage = job.getServiceComponent().getPackageName();
@@ -1435,9 +1440,14 @@
                 completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
-                0,
+                mExecutionStartTimeElapsed - completedJob.enqueueTime,
                 completedJob.getJob().isUserInitiated(),
-                completedJob.startedAsUserInitiatedJob);
+                completedJob.startedAsUserInitiatedJob,
+                completedJob.getJob().isPeriodic(),
+                completedJob.getJob().getMinLatencyMillis(),
+                completedJob.getEstimatedNetworkDownloadBytes(),
+                completedJob.getEstimatedNetworkUploadBytes(),
+                completedJob.getWorkCount());
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 234a93c..b9e3b76 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -33,7 +33,9 @@
 import android.app.job.JobInfo;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
@@ -66,6 +68,11 @@
             | CONSTRAINT_CHARGING
             | CONSTRAINT_IDLE;
 
+    /** List of flexible constraints a job can opt into. */
+    static final int OPTIONAL_FLEXIBLE_CONSTRAINTS = CONSTRAINT_BATTERY_NOT_LOW
+            | CONSTRAINT_CHARGING
+            | CONSTRAINT_IDLE;
+
     /** List of all job flexible constraints whose satisfaction is job specific. */
     private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY;
 
@@ -76,6 +83,9 @@
     private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
             Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
 
+    static final int NUM_OPTIONAL_FLEXIBLE_CONSTRAINTS =
+            Integer.bitCount(OPTIONAL_FLEXIBLE_CONSTRAINTS);
+
     static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
             Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
 
@@ -130,6 +140,7 @@
     final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
     @VisibleForTesting
     final FcConfig mFcConfig;
+    private final FcHandler mHandler;
     @VisibleForTesting
     final PrefetchController mPrefetchController;
 
@@ -174,9 +185,12 @@
                 }
             };
 
+    private static final int MSG_UPDATE_JOBS = 0;
+
     public FlexibilityController(
             JobSchedulerService service, PrefetchController prefetchController) {
         super(service);
+        mHandler = new FcHandler(AppSchedulingModuleThread.get().getLooper());
         mDeviceSupportsFlexConstraints = !mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_AUTOMOTIVE);
         mFlexibilityEnabled &= mDeviceSupportsFlexConstraints;
@@ -238,15 +252,16 @@
     boolean isFlexibilitySatisfiedLocked(JobStatus js) {
         return !mFlexibilityEnabled
                 || mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP
-                || mService.isCurrentlyRunningLocked(js)
                 || getNumSatisfiedFlexibleConstraintsLocked(js)
-                >= js.getNumRequiredFlexibleConstraints();
+                        >= js.getNumRequiredFlexibleConstraints()
+                || mService.isCurrentlyRunningLocked(js);
     }
 
     @VisibleForTesting
     @GuardedBy("mLock")
     int getNumSatisfiedFlexibleConstraintsLocked(JobStatus js) {
         return Integer.bitCount(mSatisfiedFlexibleConstraints & js.getPreferredConstraintFlags())
+                // Connectivity is job-specific, so must be handled separately.
                 + (js.getHasAccessToUnmetered() ? 1 : 0);
     }
 
@@ -267,33 +282,11 @@
                        + " constraint: " + constraint + " state: " + state);
             }
 
-            final int prevSatisfied = Integer.bitCount(mSatisfiedFlexibleConstraints);
             mSatisfiedFlexibleConstraints =
                     (mSatisfiedFlexibleConstraints & ~constraint) | (state ? constraint : 0);
-            final int curSatisfied = Integer.bitCount(mSatisfiedFlexibleConstraints);
-
-            // Only the max of the number of required flexible constraints will need to be updated
-            // The rest did not have a change in state and are still satisfied or unsatisfied.
-            final int numConstraintsToUpdate = Math.max(curSatisfied, prevSatisfied);
-
-            // In order to get the range of all potentially satisfied jobs, we start at the number
-            // of satisfied system-wide constraints and iterate to the max number of potentially
-            // satisfied constraints, determined by how many job-specific constraints exist.
-            for (int j = 0; j <= NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS; j++) {
-                final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
-                        .getJobsByNumRequiredConstraints(numConstraintsToUpdate + j);
-
-                if (jobsByNumConstraints == null) {
-                    // If there are no more jobs to iterate through we can just return.
-                    return;
-                }
-
-                for (int i = 0; i < jobsByNumConstraints.size(); i++) {
-                    JobStatus js = jobsByNumConstraints.valueAt(i);
-                    js.setFlexibilityConstraintSatisfied(
-                            nowElapsed, isFlexibilitySatisfiedLocked(js));
-                }
-            }
+            // Push the job update to the handler to avoid blocking other controllers and
+            // potentially batch back-to-back controller state updates together.
+            mHandler.obtainMessage(MSG_UPDATE_JOBS).sendToTarget();
         }
     }
 
@@ -630,6 +623,44 @@
         }
     }
 
+    private class FcHandler extends Handler {
+        FcHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_JOBS:
+                    removeMessages(MSG_UPDATE_JOBS);
+
+                    synchronized (mLock) {
+                        final long nowElapsed = sElapsedRealtimeClock.millis();
+                        final ArraySet<JobStatus> changedJobs = new ArraySet<>();
+
+                        for (int o = 0; o <= NUM_OPTIONAL_FLEXIBLE_CONSTRAINTS; ++o) {
+                            final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
+                                    .getJobsByNumRequiredConstraints(o);
+
+                            if (jobsByNumConstraints != null) {
+                                for (int i = 0; i < jobsByNumConstraints.size(); i++) {
+                                    final JobStatus js = jobsByNumConstraints.valueAt(i);
+                                    if (js.setFlexibilityConstraintSatisfied(
+                                            nowElapsed, isFlexibilitySatisfiedLocked(js))) {
+                                        changedJobs.add(js);
+                                    }
+                                }
+                            }
+                        }
+                        if (changedJobs.size() > 0) {
+                            mStateChangedListener.onControllerStateChanged(changedJobs);
+                        }
+                    }
+                    break;
+            }
+        }
+    }
+
     @VisibleForTesting
     class FcConfig {
         private boolean mShouldReevaluateConstraints = false;
@@ -651,7 +682,7 @@
         static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
                 FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
 
-        private static final boolean DEFAULT_FLEXIBILITY_ENABLED = true;
+        private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
         @VisibleForTesting
         static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
         @VisibleForTesting
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 7cc2f28..6445c3b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -590,9 +590,10 @@
             this.sourceTag = tag;
         }
 
+        final String bnNamespace = namespace == null ? "" :  "@" + namespace + "@";
         this.batteryName = this.sourceTag != null
-                ? this.sourceTag + ":" + job.getService().getPackageName()
-                : job.getService().flattenToShortString();
+                ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
+                : bnNamespace + job.getService().flattenToShortString();
         this.tag = "*job*/" + this.batteryName + "#" + job.getId();
 
         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index aca0a6e..175c8d1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -35,7 +35,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
-import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -382,31 +382,11 @@
                 }
             };
 
-    private class QcUidObserver extends IUidObserver.Stub {
+    private class QcUidObserver extends UidObserver {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
             mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget();
         }
-
-        @Override
-        public void onUidGone(int uid, boolean disabled) {
-        }
-
-        @Override
-        public void onUidActive(int uid) {
-        }
-
-        @Override
-        public void onUidIdle(int uid, boolean disabled) {
-        }
-
-        @Override
-        public void onUidCachedChanged(int uid, boolean cached) {
-        }
-
-        @Override
-        public void onUidProcAdjChanged(int uid) {
-        }
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
index 3578c8a..58536675 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.os.RemoteException;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -61,7 +62,7 @@
     @GuardedBy("mLock")
     private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
 
-    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+    private final IUidObserver mUidObserver = new UidObserver() {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
             final int newBucket = getProcStateBucket(procState);
@@ -85,22 +86,6 @@
                 notifyStateChangedLocked(uid);
             }
         }
-
-        @Override
-        public void onUidActive(int uid) {
-        }
-
-        @Override
-        public void onUidIdle(int uid, boolean disabled) {
-        }
-
-        @Override
-        public void onUidCachedChanged(int uid, boolean cached) {
-        }
-
-        @Override
-        public void onUidProcAdjChanged(int uid) {
-        }
     };
 
     ProcessStateModifier(@NonNull InternalResourceService irs) {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1346d07..873234a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -427,7 +427,7 @@
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
     method public void expandNotificationsPanel();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getLastSystemKey();
-    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(int);
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(@NonNull android.view.KeyEvent);
     method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void togglePanel();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 29f774c..a6313db 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -46,6 +46,7 @@
 import android.os.UserHandle;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.KeyEvent;
 import android.view.View;
 
 import com.android.internal.statusbar.AppClipsServiceConnector;
@@ -740,7 +741,7 @@
      */
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     @TestApi
-    public void handleSystemKey(int key) {
+    public void handleSystemKey(@NonNull KeyEvent key) {
         try {
             final IStatusBarService svc = getService();
             if (svc != null) {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 12882a2..9efdf28 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -39,21 +39,22 @@
 import android.os.ResultReceiver;
 
 /**
- * Interface for a virtual device.
+ * Interface for a virtual device for communication between the system server and the process of
+ * the owner of the virtual device.
  *
  * @hide
  */
 interface IVirtualDevice {
 
     /**
-     * Returns the association ID for this virtual device.
+     * Returns the CDM association ID of this virtual device.
      *
      * @see AssociationInfo#getId()
      */
     int getAssociationId();
 
     /**
-     * Returns the unique device ID for this virtual device.
+     * Returns the unique ID of this virtual device.
      */
     int getDeviceId();
 
@@ -64,55 +65,99 @@
     void close();
 
     /**
-     * Notifies of an audio session being started.
+     * Notifies that an audio session being started.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void onAudioSessionStarting(
-            int displayId,
-            IAudioRoutingCallback routingCallback,
+    void onAudioSessionStarting(int displayId, IAudioRoutingCallback routingCallback,
             IAudioConfigChangedCallback configChangedCallback);
 
+    /**
+     * Notifies that an audio session has ended.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void onAudioSessionEnded();
 
+    /**
+     * Creates a new dpad and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualDpad(
-            in VirtualDpadConfig config,
-            IBinder token);
+    void createVirtualDpad(in VirtualDpadConfig config, IBinder token);
+
+    /**
+     * Creates a new keyboard and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualKeyboard(
-            in VirtualKeyboardConfig config,
-            IBinder token);
+    void createVirtualKeyboard(in VirtualKeyboardConfig config, IBinder token);
+
+    /**
+     * Creates a new mouse and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualMouse(
-            in VirtualMouseConfig config,
-            IBinder token);
+    void createVirtualMouse(in VirtualMouseConfig config, IBinder token);
+
+    /**
+     * Creates a new touchscreen and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualTouchscreen(
-            in VirtualTouchscreenConfig config,
-            IBinder token);
+    void createVirtualTouchscreen(in VirtualTouchscreenConfig config, IBinder token);
+
+    /**
+     * Creates a new navigation touchpad and registers it with the input framework with the given
+     * token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualNavigationTouchpad(
-            in VirtualNavigationTouchpadConfig config,
-            IBinder token);
+    void createVirtualNavigationTouchpad(in VirtualNavigationTouchpadConfig config, IBinder token);
+
+    /**
+     * Removes the input device corresponding to the given token from the framework.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterInputDevice(IBinder token);
+
+    /**
+     * Returns the ID of the device corresponding to the given token, as registered with the input
+     * framework.
+     */
     int getInputDeviceId(IBinder token);
+
+    /**
+    * Injects a key event to the virtual dpad corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event);
+
+    /**
+    * Injects a key event to the virtual keyboard corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
+
+    /**
+    * Injects a button event to the virtual mouse corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
+
+    /**
+    * Injects a relative event to the virtual mouse corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
+
+    /**
+    * Injects a scroll event to the virtual mouse corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
+
+    /**
+    * Injects a touch event to the virtual touch input device corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
 
     /**
-     * Returns all virtual sensors for this device.
+     * Returns all virtual sensors created for this device.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     List<VirtualSensor> getVirtualSensorList();
@@ -126,8 +171,13 @@
     /**
      * Launches a pending intent on the given display that is owned by this virtual device.
      */
-    void launchPendingIntent(
-            int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
+    void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
+            in ResultReceiver resultReceiver);
+
+    /**
+     * Returns the current cursor position of the mouse corresponding to the given token, in x and y
+     * coordinates.
+     */
     PointF getCursorPosition(IBinder token);
 
     /** Sets whether to show or hide the cursor while this virtual device is active. */
@@ -140,8 +190,12 @@
      * intent.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void registerIntentInterceptor(
-            in IVirtualDeviceIntentInterceptor intentInterceptor, in IntentFilter filter);
+    void registerIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor,
+            in IntentFilter filter);
+
+    /**
+     * Unregisters a previously registered intent interceptor.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 4f49b8d..07743cef5 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -101,7 +101,7 @@
      *
      * @param deviceId id of the virtual device.
      * @param sound effect type corresponding to
-     *     {@code android.media.AudioManager.SystemSoundEffect}
+     *   {@code android.media.AudioManager.SystemSoundEffect}
      */
     void playSoundEffect(int deviceId, int effectType);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
index 91c209f..f284554 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
@@ -28,7 +28,7 @@
      * Called when there's sound effect to be played on Virtual Device.
      *
      * @param sound effect type corresponding to
-     *     {@code android.media.AudioManager.SystemSoundEffect}
+     *   {@code android.media.AudioManager.SystemSoundEffect}
      */
     void onPlaySoundEffect(int effectType);
 }
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index 4a09186..4ee65e0 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -26,6 +26,11 @@
 
 /**
  * Details of a particular virtual device.
+ *
+ * <p>Read-only device representation exposing the properties of an existing virtual device.
+ *
+ * <p class="note">Not to be confused with {@link VirtualDeviceManager.VirtualDevice}, which is used
+ * by the virtual device creator and allows them to manage the device.
  */
 public final class VirtualDevice implements Parcelable {
 
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d3d635e..da6784b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -68,7 +68,13 @@
 import java.util.function.IntConsumer;
 
 /**
- * System level service for managing virtual devices.
+ * System level service for creation and management of virtual devices.
+ *
+ * <p>VirtualDeviceManager enables interactive sharing of capabilities between the host Android
+ * device and a remote device.
+ *
+ * <p class="note">Not to be confused with the Android Studio's Virtual Device Manager, which allows
+ * for device emulation.
  */
 @SystemService(Context.VIRTUAL_DEVICE_SERVICE)
 public final class VirtualDeviceManager {
@@ -174,6 +180,9 @@
 
     /**
      * Returns the details of all available virtual devices.
+     *
+     * <p>The returned objects are read-only representations that expose the properties of all
+     * existing virtual devices.
      */
     @NonNull
     public List<android.companion.virtual.VirtualDevice> getVirtualDevices() {
@@ -252,11 +261,12 @@
      *
      * @param deviceId - id of the virtual audio device
      * @return Device specific session id to be used for audio playback (see
-     *     {@link android.media.AudioManager.generateAudioSessionId}) if virtual device has
-     *     {@link VirtualDeviceParams.POLICY_TYPE_AUDIO} set to
-     *     {@link VirtualDeviceParams.DEVICE_POLICY_CUSTOM} and Virtual Audio Device
-     *     is configured in context-aware mode.
-     *     Otherwise {@link AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *   {@link AudioManager#generateAudioSessionId}) if virtual device has
+     *   {@link VirtualDeviceParams#POLICY_TYPE_AUDIO} set to
+     *   {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM} and Virtual Audio Device
+     *   is configured in context-aware mode. Otherwise
+     *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *
      * @hide
      */
     public int getAudioPlaybackSessionId(int deviceId) {
@@ -275,11 +285,12 @@
      *
      * @param deviceId - id of the virtual audio device
      * @return Device specific session id to be used for audio recording (see
-     *     {@link android.media.AudioManager.generateAudioSessionId}) if virtual device has
-     *     {@link VirtualDeviceParams.POLICY_TYPE_AUDIO} set to
-     *     {@link VirtualDeviceParams.DEVICE_POLICY_CUSTOM} and Virtual Audio Device
-     *     is configured in context-aware mode.
-     *     Otherwise {@link AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *   {@link AudioManager#generateAudioSessionId}) if virtual device has
+     *   {@link VirtualDeviceParams#POLICY_TYPE_AUDIO} set to
+     *   {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM} and Virtual Audio Device
+     *   is configured in context-aware mode. Otherwise
+     *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *
      * @hide
      */
     public int getAudioRecordingSessionId(int deviceId) {
@@ -296,10 +307,11 @@
     /**
      * Requests sound effect to be played on virtual device.
      *
-     * @see android.media.AudioManager#playSoundEffect(int)
+     * @see AudioManager#playSoundEffect(int)
      *
      * @param deviceId - id of the virtual audio device
      * @param effectType the type of sound effect
+     *
      * @hide
      */
     public void playSoundEffect(int deviceId, @AudioManager.SystemSoundEffect int effectType) {
@@ -315,11 +327,18 @@
     }
 
     /**
-     * A virtual device has its own virtual display, audio output, microphone, sensors, etc. The
-     * creator of a virtual device can take the output from the virtual display and stream it over
-     * to another device, and inject input events that are received from the remote device.
+     * A representation of a virtual device.
      *
-     * TODO(b/204081582): Consider using a builder pattern for the input APIs.
+     * <p>A virtual device can have its own virtual displays, audio input/output, sensors, etc.
+     * The creator of a virtual device can take the output from the virtual display and stream it
+     * over to another device, and inject input and sensor events that are received from the remote
+     * device.
+     *
+     * <p>This object is only used by the virtual device creator and allows them to manage the
+     * device's behavior, peripherals, and the user interaction with that device.
+     *
+     * <p class="note">Not to be confused with {@link android.companion.virtual.VirtualDevice},
+     * which is a read-only representation exposing the properties of an existing virtual device.
      *
      * @hide
      */
@@ -346,8 +365,10 @@
         }
 
         /**
-         * @return A new Context bound to this device. This is a convenience method equivalent to
-         * calling {@link Context#createDeviceContext(int)} with the device id of this device.
+         * Returns a new context bound to this device.
+         *
+         * <p>This is a convenience method equivalent to calling
+         * {@link Context#createDeviceContext(int)} with the id of this device.
          */
         public @NonNull Context createContext() {
             return mVirtualDeviceInternal.createContext();
@@ -400,20 +421,19 @@
          * @param height The height of the virtual display in pixels, must be greater than 0.
          * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
          * @param surface The surface to which the content of the virtual display should
-         * be rendered, or null if there is none initially. The surface can also be set later using
-         * {@link VirtualDisplay#setSurface(Surface)}.
+         *   be rendered, or null if there is none initially. The surface can also be set later
+         *   using {@link VirtualDisplay#setSurface(Surface)}.
          * @param flags A combination of virtual display flags accepted by
-         * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
-         * automatically set for all virtual devices:
-         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
-         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
-         * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+         *   {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
+         *   automatically set for all virtual devices:
+         *   {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC} and
+         *   {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
          * @param executor The executor on which {@code callback} will be invoked. This is ignored
-         * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
-         * not be null.
+         *   if {@code callback} is {@code null}. If {@code callback} is specified, this executor
+         *   must not be null.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @return The newly created virtual display, or {@code null} if the application could
-         * not create the virtual display.
+         *   not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
          *
@@ -450,11 +470,11 @@
          *
          * @param config The configuration of the display.
          * @param executor The executor on which {@code callback} will be invoked. This is ignored
-         * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
-         * not be null.
+         *   if {@code callback} is {@code null}. If {@code callback} is specified, this executor
+         *   must not be null.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @return The newly created virtual display, or {@code null} if the application could
-         * not create the virtual display.
+         *   not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
          */
@@ -478,7 +498,7 @@
         /**
          * Creates a virtual dpad.
          *
-         * @param config the configurations of the virtual Dpad.
+         * @param config the configurations of the virtual dpad.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -500,11 +520,10 @@
         /**
          * Creates a virtual keyboard.
          *
-         * @param display         the display that the events inputted through this device should
-         *                        target
-         * @param inputDeviceName the name to call this input device
-         * @param vendorId        the PCI vendor id
-         * @param productId       the product id, as defined by the vendor
+         * @param display the display that the events inputted through this device should target.
+         * @param inputDeviceName the name of this keyboard device.
+         * @param vendorId the PCI vendor id.
+         * @param productId the product id, as defined by the vendor.
          * @see #createVirtualKeyboard(VirtualKeyboardConfig config)
          * @deprecated Use {@link #createVirtualKeyboard(VirtualKeyboardConfig config)} instead
          */
@@ -537,14 +556,12 @@
         /**
          * Creates a virtual mouse.
          *
-         * @param display         the display that the events inputted through this device should
-         *                        target
-         * @param inputDeviceName the name to call this input device
-         * @param vendorId        the PCI vendor id
-         * @param productId       the product id, as defined by the vendor
+         * @param display the display that the events inputted through this device should target.
+         * @param inputDeviceName the name of this mouse.
+         * @param vendorId the PCI vendor id.
+         * @param productId the product id, as defined by the vendor.
          * @see #createVirtualMouse(VirtualMouseConfig config)
          * @deprecated Use {@link #createVirtualMouse(VirtualMouseConfig config)} instead
-         * *
          */
         @Deprecated
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -576,11 +593,10 @@
         /**
          * Creates a virtual touchscreen.
          *
-         * @param display         the display that the events inputted through this device should
-         *                        target
-         * @param inputDeviceName the name to call this input device
-         * @param vendorId        the PCI vendor id
-         * @param productId       the product id, as defined by the vendor
+         * @param display the display that the events inputted through this device should target.
+         * @param inputDeviceName the name of this touchscreen device.
+         * @param vendorId the PCI vendor id.
+         * @param productId the product id, as defined by the vendor.
          * @see #createVirtualTouchscreen(VirtualTouchscreenConfig config)
          * @deprecated Use {@link #createVirtualTouchscreen(VirtualTouchscreenConfig config)}
          * instead
@@ -605,11 +621,13 @@
         /**
          * Creates a virtual touchpad in navigation mode.
          *
-         * A touchpad in navigation mode means that its events are interpreted as navigation events
-         * (up, down, etc) instead of using them to update a cursor's absolute position. If the
-         * events are not consumed they are converted to DPAD events.
+         * <p>A touchpad in navigation mode means that its events are interpreted as navigation
+         * events (up, down, etc) instead of using them to update a cursor's absolute position. If
+         * the events are not consumed they are converted to DPAD events and delivered to the target
+         * again.
          *
          * @param config the configurations of the virtual navigation touchpad.
+         * @see android.view.InputDevice#SOURCE_TOUCH_NAVIGATION
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -629,10 +647,10 @@
          *
          * @param display The target virtual display to capture from and inject into.
          * @param executor The {@link Executor} object for the thread on which to execute
-         *                the callback. If <code>null</code>, the {@link Executor} associated with
-         *                the main {@link Looper} will be used.
+         *   the callback. If <code>null</code>, the {@link Executor} associated with the main
+         *   {@link Looper} will be used.
          * @param callback Interface to be notified when playback or recording configuration of
-         *                applications running on virtual display is changed.
+         *   applications running on virtual display is changed.
          * @return A {@link VirtualAudioDevice} instance.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -648,7 +666,7 @@
          * Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
          *
          * @param showPointerIcon True if the pointer should be shown; false otherwise. The default
-         *                        visibility is true.
+         *   visibility is true.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -670,8 +688,7 @@
         }
 
         /**
-         * Removes an activity listener previously added with
-         * {@link #addActivityListener}.
+         * Removes an activity listener previously added with {@link #addActivityListener}.
          *
          * @param listener The listener to remove.
          * @see #addActivityListener(Executor, ActivityListener)
@@ -693,10 +710,10 @@
         }
 
         /**
-         * Removes a sound effect listener previously added with {@link #addActivityListener}.
+         * Removes a sound effect listener previously added with {@link #addSoundEffectListener}.
          *
          * @param soundEffectListener The listener to remove.
-         * @see #addActivityListener(Executor, ActivityListener)
+         * @see #addSoundEffectListener(Executor, SoundEffectListener)
          */
         public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) {
             mVirtualDeviceInternal.removeSoundEffectListener(soundEffectListener);
@@ -723,7 +740,7 @@
         }
 
         /**
-         * Unregisters the intent interceptorCallback previously registered with
+         * Unregisters the intent interceptor previously registered with
          * {@link #registerIntentInterceptor}.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -761,9 +778,9 @@
          * {@link #onDisplayEmpty(int)} will be called. If the value topActivity is cached, it
          * should be cleared when {@link #onDisplayEmpty(int)} is called.
          *
-         * @param displayId   The display ID on which the activity change happened.
+         * @param displayId The display ID on which the activity change happened.
          * @param topActivity The component name of the top activity.
-         * @param userId      The user ID associated with the top activity.
+         * @param userId The user ID associated with the top activity.
          */
         default void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
                 @UserIdInt int userId) {}
@@ -800,6 +817,7 @@
 
     /**
      * Listener for system sound effect playback on virtual device.
+     *
      * @hide
      */
     @SystemApi
@@ -808,8 +826,8 @@
         /**
          * Called when there's a system sound effect to be played on virtual device.
          *
-         * @param effectType - system sound effect type, see
-         *     {@code android.media.AudioManager.SystemSoundEffect}
+         * @param effectType - system sound effect type
+         * @see android.media.AudioManager.SystemSoundEffect
          */
         void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType);
     }
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 9a34dbe..45d6dc6 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -34,6 +34,7 @@
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
 import android.content.ComponentName;
+import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SharedMemory;
@@ -680,7 +681,7 @@
          * {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch
          * unless they are in {@code blockedCrossTaskNavigations}.
          *
-         * <p> This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
+         * <p>This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
          * been called.
          *
          * @throws IllegalArgumentException if {@link #setAllowedCrossTaskNavigations(Set)} has
@@ -847,11 +848,11 @@
          * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO},
          * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if
          * the playback session id is set to value other than
-         * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}.
+         * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}.
          *
          * @param playbackSessionId requested device-specific audio session id for playback
-         * @see android.media.AudioManager.generateAudioSessionId()
-         * @see android.media.AudioTrack.Builder.setContext(Context)
+         * @see android.media.AudioManager#generateAudioSessionId()
+         * @see android.media.AudioTrack.Builder#setContext(Context)
          */
         @NonNull
         public Builder setAudioPlaybackSessionId(int playbackSessionId) {
@@ -871,11 +872,11 @@
          * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO},
          * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if
          * the recording session id is set to value other than
-         * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}.
+         * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}.
          *
          * @param recordingSessionId requested device-specific audio session id for playback
-         * @see android.media.AudioManager.generateAudioSessionId()
-         * @see android.media.AudioRecord.Builder.setContext(Context)
+         * @see android.media.AudioManager#generateAudioSessionId()
+         * @see android.media.AudioRecord.Builder#setContext(Context)
          */
         @NonNull
         public Builder setAudioRecordingSessionId(int recordingSessionId) {
diff --git a/core/java/android/companion/virtual/audio/AudioCapture.java b/core/java/android/companion/virtual/audio/AudioCapture.java
index d6d0d2b..dd5e660 100644
--- a/core/java/android/companion/virtual/audio/AudioCapture.java
+++ b/core/java/android/companion/virtual/audio/AudioCapture.java
@@ -56,12 +56,12 @@
 
     /**
      * Sets the {@link AudioRecord} to handle audio capturing.
-     * Callers may call this multiple times with different audio records to change
-     * the underlying {@link AudioRecord} without stopping and re-starting recording.
      *
-     * @param audioRecord The underlying {@link AudioRecord} to use for capture,
-     * or null if no audio (i.e. silence) should be captured while still keeping the
-     * record in a recording state.
+     * <p>Callers may call this multiple times with different audio records to change the underlying
+     * {@link AudioRecord} without stopping and re-starting recording.
+     *
+     * @param audioRecord The underlying {@link AudioRecord} to use for capture, or null if no audio
+     *   (i.e. silence) should be captured while still keeping the record in a recording state.
      */
     void setAudioRecord(@Nullable AudioRecord audioRecord) {
         Log.d(TAG, "set AudioRecord with " + audioRecord);
diff --git a/core/java/android/companion/virtual/audio/AudioInjection.java b/core/java/android/companion/virtual/audio/AudioInjection.java
index 9d6a3eb..5de5f7e 100644
--- a/core/java/android/companion/virtual/audio/AudioInjection.java
+++ b/core/java/android/companion/virtual/audio/AudioInjection.java
@@ -65,12 +65,12 @@
 
     /**
      * Sets the {@link AudioTrack} to handle audio injection.
-     * Callers may call this multiple times with different audio tracks to change
-     * the underlying {@link AudioTrack} without stopping and re-starting injection.
      *
-     * @param audioTrack The underlying {@link AudioTrack} to use for injection,
-     * or null if no audio (i.e. silence) should be injected while still keeping the
-     * record in a playing state.
+     * <p>Callers may call this multiple times with different audio tracks to change the underlying
+     * {@link AudioTrack} without stopping and re-starting injection.
+     *
+     * @param audioTrack The underlying {@link AudioTrack} to use for injection, or null if no audio
+     *   (i.e. silence) should be injected while still keeping the record in a playing state.
      */
     void setAudioTrack(@Nullable AudioTrack audioTrack) {
         Log.d(TAG, "set AudioTrack with " + audioTrack);
diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
index 3cb0572..dcdb6c6 100644
--- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
+++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
@@ -33,7 +33,7 @@
      * @param enabled Whether the sensor is enabled.
      * @param samplingPeriodMicros The requested sensor's sampling period in microseconds.
      * @param batchReportingLatencyMicros The requested maximum time interval in microseconds
-     * between the delivery of two batches of sensor events.
+     *   between the delivery of two batches of sensor events.
      */
     void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros,
             int batchReportLatencyMicros);
@@ -60,7 +60,7 @@
      * @param sensor The sensor, for which the channel was configured.
      * @param rateLevel The rate level used to configure the direct sensor channel.
      * @param reportToken A positive sensor report token, used to differentiate between events from
-     * different sensors within the same channel.
+     *   different sensors within the same channel.
      */
     void onDirectChannelConfigured(int channelHandle, in VirtualSensor sensor, int rateLevel,
             int reportToken);
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index bda44d4..eaa1792 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -30,7 +30,7 @@
  * Representation of a sensor on a remote device, capable of sending events, such as an
  * accelerometer or a gyroscope.
  *
- * This registers the sensor device with the sensor framework as a runtime sensor.
+ * <p>A virtual sensor device is registered with the sensor framework as a runtime sensor.
  *
  * @hide
  */
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
index e6bd6da..4d586f6 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
@@ -45,10 +45,10 @@
      *
      * @param sensor The sensor whose requested injection parameters have changed.
      * @param enabled Whether the sensor is enabled. True if any listeners are currently registered,
-     * and false otherwise.
+     *   and false otherwise.
      * @param samplingPeriod The requested sampling period of the sensor.
      * @param batchReportLatency The requested maximum time interval between the delivery of two
-     * batches of sensor events.
+     *   batches of sensor events.
      */
     void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled,
             @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency);
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index ef55ca9..3bdf9aa 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -31,7 +31,9 @@
 
 /**
  * Configuration for creation of a virtual sensor.
+ *
  * @see VirtualSensor
+ *
  * @hide
  */
 @SystemApi
@@ -122,6 +124,7 @@
 
     /**
      * Returns the vendor string of the sensor.
+     *
      * @see Builder#setVendor
      */
     @Nullable
@@ -130,7 +133,8 @@
     }
 
     /**
-     * Returns maximum range of the sensor in the sensor's unit.
+     * Returns the maximum range of the sensor in the sensor's unit.
+     *
      * @see Sensor#getMaximumRange
      */
     public float getMaximumRange() {
@@ -138,7 +142,8 @@
     }
 
     /**
-     * Returns The resolution of the sensor in the sensor's unit.
+     * Returns the resolution of the sensor in the sensor's unit.
+     *
      * @see Sensor#getResolution
      */
     public float getResolution() {
@@ -146,7 +151,8 @@
     }
 
     /**
-     * Returns The power in mA used by this sensor while in use.
+     * Returns the power in mA used by this sensor while in use.
+     *
      * @see Sensor#getPower
      */
     public float getPower() {
@@ -154,8 +160,9 @@
     }
 
     /**
-     * Returns The minimum delay allowed between two events in microseconds, or zero depending on
+     * Returns the minimum delay allowed between two events in microseconds, or zero depending on
      * the sensor type.
+     *
      * @see Sensor#getMinDelay
      */
     public int getMinDelay() {
@@ -163,7 +170,8 @@
     }
 
     /**
-     * Returns The maximum delay between two sensor events in microseconds.
+     * Returns the maximum delay between two sensor events in microseconds.
+     *
      * @see Sensor#getMaxDelay
      */
     public int getMaxDelay() {
@@ -201,6 +209,7 @@
 
     /**
      * Returns the sensor flags.
+     *
      * @hide
      */
     public int getFlags() {
@@ -233,7 +242,7 @@
          *
          * @param type The type of the sensor, matching {@link Sensor#getType}.
          * @param name The name of the sensor. Must be unique among all sensors with the same type
-         *             that belong to the same virtual device.
+         *   that belong to the same virtual device.
          */
         public Builder(@IntRange(from = 1) int type, @NonNull String name) {
             if (type <= 0) {
@@ -275,6 +284,7 @@
 
         /**
          * Sets the maximum range of the sensor in the sensor's unit.
+         *
          * @see Sensor#getMaximumRange
          */
         @NonNull
@@ -285,6 +295,7 @@
 
         /**
          * Sets the resolution of the sensor in the sensor's unit.
+         *
          * @see Sensor#getResolution
          */
         @NonNull
@@ -295,6 +306,7 @@
 
         /**
          * Sets the power in mA used by this sensor while in use.
+         *
          * @see Sensor#getPower
          */
         @NonNull
@@ -305,6 +317,7 @@
 
         /**
          * Sets the minimum delay allowed between two events in microseconds.
+         *
          * @see Sensor#getMinDelay
          */
         @NonNull
@@ -315,6 +328,7 @@
 
         /**
          * Sets the maximum delay between two sensor events in microseconds.
+         *
          * @see Sensor#getMaxDelay
          */
         @NonNull
@@ -339,11 +353,11 @@
          * Sets whether direct sensor channel of the given types is supported.
          *
          * @param memoryTypes A combination of {@link SensorDirectChannel.MemoryType} flags
-         * indicating the types of shared memory supported for creating direct channels. Only
-         * {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for virtual
-         * sensors.
+         *   indicating the types of shared memory supported for creating direct channels. Only
+         *   {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for
+         *   virtual sensors.
          * @throws IllegalArgumentException if {@link SensorDirectChannel#TYPE_HARDWARE_BUFFER} is
-         * set to be supported.
+         *   set to be supported.
          */
         @NonNull
         public VirtualSensorConfig.Builder setDirectChannelTypesSupported(
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
index d352f94f..f10e9d0 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
@@ -45,6 +45,8 @@
  * <p>The callback is tied to the VirtualDevice's lifetime as the virtual sensors are created when
  * the device is created and destroyed when the device is destroyed.
  *
+ * @see VirtualSensorDirectChannelWriter
+ *
  * @hide
  */
 @SystemApi
@@ -94,7 +96,7 @@
      * @param sensor The sensor, for which the channel was configured.
      * @param rateLevel The rate level used to configure the direct sensor channel.
      * @param reportToken A positive sensor report token, used to differentiate between events from
-     * different sensors within the same channel.
+     *   different sensors within the same channel.
      *
      * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int)
      * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int)
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
index 6aed96f..bf78dd0 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
@@ -41,6 +41,41 @@
  * write the events from the relevant sensors directly to the shared memory regions of the
  * corresponding {@link SensorDirectChannel} instances.
  *
+ * <p>Example:
+ * <p>During sensor and virtual device creation:
+ * <pre>
+ * VirtualSensorDirectChannelWriter writer = new VirtualSensorDirectChannelWriter();
+ * VirtualSensorDirectChannelCallback callback = new VirtualSensorDirectChannelCallback() {
+ *     @Override
+ *     public void onDirectChannelCreated(int channelHandle, SharedMemory sharedMemory) {
+ *         writer.addChannel(channelHandle, sharedMemory);
+ *     }
+ *     @Override
+ *     public void onDirectChannelDestroyed(int channelHandle);
+ *         writer.removeChannel(channelHandle);
+ *     }
+ *     @Override
+ *     public void onDirectChannelConfigured(int channelHandle, VirtualSensor sensor, int rateLevel,
+ *             int reportToken)
+ *         if (!writer.configureChannel(channelHandle, sensor, rateLevel, reportToken)) {
+ *              // handle error
+ *         }
+ *     }
+ * }
+ * </pre>
+ * <p>During the virtual device lifetime:
+ * <pre>
+ * VirtualSensor sensor = ...
+ * while (shouldInjectEvents(sensor)) {
+ *     if (!writer.writeSensorEvent(sensor, event)) {
+ *         // handle error
+ *     }
+ * }
+ * writer.close();
+ * </pre>
+ * <p>Note that the virtual device owner should take the currently configured rate level into
+ * account when deciding whether and how often to inject events for a particular sensor.
+ *
  * @see android.hardware.SensorDirectChannel#configure
  * @see VirtualSensorDirectChannelCallback
  *
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java b/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
index 01b4975..a368467e 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
@@ -121,7 +121,7 @@
          * monotonically increasing using the same time base as
          * {@link android.os.SystemClock#elapsedRealtimeNanos()}.
          *
-         * If not explicitly set, the current timestamp is used for the sensor event.
+         * <p>If not explicitly set, the current timestamp is used for the sensor event.
          *
          * @see android.hardware.SensorEvent#timestamp
          */
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 0e4c3c0..e908ced 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -41,9 +41,12 @@
  * <p>The properties describing a
  * {@link CameraDevice CameraDevice}.</p>
  *
- * <p>These properties are fixed for a given CameraDevice, and can be queried
+ * <p>These properties are primarily fixed for a given CameraDevice, and can be queried
  * through the {@link CameraManager CameraManager}
- * interface with {@link CameraManager#getCameraCharacteristics}.</p>
+ * interface with {@link CameraManager#getCameraCharacteristics}. Beginning with API level 32, some
+ * properties such as {@link #SENSOR_ORIENTATION} may change dynamically based on the state of the
+ * device. For information on whether a specific value is fixed, see the documentation for its key.
+ * </p>
  *
  * <p>When obtained by a client that does not hold the CAMERA permission, some metadata values are
  * not included. The list of keys that require the permission is given by
@@ -281,9 +284,6 @@
      * <p>The field definitions can be
      * found in {@link CameraCharacteristics}.</p>
      *
-     * <p>Querying the value for the same key more than once will return a value
-     * which is equal to the previous queried value.</p>
-     *
      * @throws IllegalArgumentException if the key was not valid
      *
      * @param key The characteristics field to read.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index a7e28e2..4950373 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -41,9 +41,10 @@
  * </p>
  *
  * <p>
- * All instances of CameraMetadata are immutable. The list of keys with {@link #getKeys()}
- * never changes, nor do the values returned by any key with {@code #get} throughout
- * the lifetime of the object.
+ * All instances of CameraMetadata are immutable. Beginning with API level 32, the list of keys
+ * returned by {@link #getKeys()} may change depending on the state of the device, as may the
+ * values returned by any key with {@code #get} throughout the lifetime of the object. For
+ * information on whether a specific value is fixed, see the documentation for its key.
  * </p>
  *
  * @see CameraDevice
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index e7385b6..9cacfff 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Vibrator;
+import android.sysprop.InputProperties;
 import android.util.Log;
 import android.view.Display;
 import android.view.InputDevice;
@@ -1123,7 +1124,8 @@
     public boolean isStylusPointerIconEnabled() {
         if (mIsStylusPointerIconEnabled == null) {
             mIsStylusPointerIconEnabled = getContext().getResources()
-                    .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon);
+                    .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
+                    || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
         }
         return mIsStylusPointerIconEnabled;
     }
diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index 01b2cb9..1e41886 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 38b3174..46cf016 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -354,6 +354,7 @@
     }
 
     /** @hide */
+    @Override
     public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
         return Collections.unmodifiableMap(new HashMap<>(mCapabilitiesMatchCriteria));
     }
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index 6f9c9dd..a27e923 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -16,6 +16,7 @@
 package android.net.vcn;
 
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -75,6 +76,7 @@
     static {
         ALLOWED_TRANSPORTS.add(TRANSPORT_WIFI);
         ALLOWED_TRANSPORTS.add(TRANSPORT_CELLULAR);
+        ALLOWED_TRANSPORTS.add(TRANSPORT_TEST);
     }
 
     private static final String PACKAGE_NAME_KEY = "mPackageName";
@@ -155,6 +157,11 @@
                                 + transport
                                 + " which might be from a new version of VcnConfig");
             }
+
+            if (transport == TRANSPORT_TEST && !mIsTestModeProfile) {
+                throw new IllegalArgumentException(
+                        "Found TRANSPORT_TEST in a non-test-mode profile");
+            }
         }
     }
 
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 9235d09..edf2c09 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -29,6 +29,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -307,4 +308,7 @@
     public int getMinExitDownstreamBandwidthKbps() {
         return mMinExitDownstreamBandwidthKbps;
     }
+
+    /** @hide */
+    public abstract Map<Integer, Integer> getCapabilitiesMatchCriteria();
 }
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 2544a6d..2e6b09f 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -15,6 +15,9 @@
  */
 package android.net.vcn;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
 import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
@@ -23,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
 
@@ -32,6 +36,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -162,6 +167,12 @@
         return Collections.unmodifiableSet(mSsids);
     }
 
+    /** @hide */
+    @Override
+    public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
+        return Collections.singletonMap(NET_CAPABILITY_INTERNET, MATCH_REQUIRED);
+    }
+
     /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
     public static final class Builder {
         private int mMeteredMatchCriteria = MATCH_ANY;
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index bfc5afe..4bfff16 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8434,7 +8434,7 @@
             extras.putString(KEY_ACCOUNT_NAME, accountName);
             extras.putString(KEY_ACCOUNT_TYPE, accountType);
 
-            contentResolver.call(ContactsContract.AUTHORITY_URI,
+            nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
                     ContactsContract.SimContacts.ADD_SIM_ACCOUNT_METHOD,
                     null, extras);
         }
@@ -8457,7 +8457,7 @@
             Bundle extras = new Bundle();
             extras.putInt(KEY_SIM_SLOT_INDEX, simSlotIndex);
 
-            contentResolver.call(ContactsContract.AUTHORITY_URI,
+            nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
                     ContactsContract.SimContacts.REMOVE_SIM_ACCOUNT_METHOD,
                     null, extras);
         }
@@ -8469,7 +8469,7 @@
          */
         public static @NonNull List<SimAccount> getSimAccounts(
                 @NonNull ContentResolver contentResolver) {
-            Bundle response = contentResolver.call(ContactsContract.AUTHORITY_URI,
+            Bundle response = nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
                     ContactsContract.SimContacts.QUERY_SIM_ACCOUNTS_METHOD,
                     null, null);
             List<SimAccount> result = response.getParcelableArrayList(KEY_SIM_ACCOUNTS, android.provider.ContactsContract.SimAccount.class);
@@ -9088,7 +9088,8 @@
          * @param contactId the id of the contact to undemote.
          */
         public static void undemote(ContentResolver contentResolver, long contactId) {
-            contentResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+            nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
+                    PinnedPositions.UNDEMOTE_METHOD,
                     String.valueOf(contactId), null);
         }
 
@@ -10300,4 +10301,13 @@
         public static final String CONTENT_ITEM_TYPE =
                 "vnd.android.cursor.item/contact_metadata_sync_state";
     }
+
+    private static Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull Uri uri,
+            @NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(uri)) {
+            return client.call(method, arg, extras);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/util/TeeWriter.java b/core/java/android/util/TeeWriter.java
new file mode 100644
index 0000000..439a0c2
--- /dev/null
+++ b/core/java/android/util/TeeWriter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Objects;
+
+/**
+ * Writer that offers to "tee" identical output to multiple underlying
+ * {@link Writer} instances.
+ *
+ * @see https://man7.org/linux/man-pages/man1/tee.1.html
+ * @hide
+ */
+public class TeeWriter extends Writer {
+    private final @NonNull Writer[] mWriters;
+
+    public TeeWriter(@NonNull Writer... writers) {
+        for (Writer writer : writers) {
+            Objects.requireNonNull(writer);
+        }
+        mWriters = writers;
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        for (Writer writer : mWriters) {
+            writer.write(cbuf, off, len);
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        for (Writer writer : mWriters) {
+            writer.flush();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        for (Writer writer : mWriters) {
+            writer.close();
+        }
+    }
+}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 24a0355..d35aff9 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -158,6 +158,14 @@
      */
     public Matrix transform;
 
+    /**
+     * The input token for the window to which focus should be transferred when this input window
+     * can be successfully focused. If null, this input window will not transfer its focus to
+     * any other window.
+     */
+    @Nullable
+    public IBinder focusTransferTarget;
+
     private native void nativeDispose();
 
     public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
@@ -195,6 +203,7 @@
             transform = new Matrix();
             transform.set(other.transform);
         }
+        focusTransferTarget = other.focusTransferTarget;
     }
 
     @Override
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 6a493e6..d88994b 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -57,8 +57,9 @@
     /** Type constant: Null icon.  It has no bitmap. */
     public static final int TYPE_NULL = 0;
 
-    /** Type constant: no icons are specified. If all views uses this, then falls back
-     * to the default type, but this is helpful to distinguish a view explicitly want
+    /**
+     * Type constant: no icons are specified. If all views uses this, then the pointer icon falls
+     * back to the default type, but this is helpful to distinguish a view that explicitly wants
      * to have the default icon.
      * @hide
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0db52aa..bc6a3b5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -265,7 +265,7 @@
             int transformHint);
     private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId);
     private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
-            String windowName, IBinder focusedToken, String focusedWindowName, int displayId);
+            String windowName, int displayId);
     private static native void nativeSetFrameTimelineVsync(long transactionObj,
             long frameTimelineVsyncId);
     private static native void nativeAddJankDataListener(long nativeListener,
@@ -3604,28 +3604,7 @@
          */
         public Transaction setFocusedWindow(@NonNull IBinder token, String windowName,
                 int displayId) {
-            nativeSetFocusedWindow(mNativeObject, token,  windowName,
-                    null /* focusedToken */, null /* focusedWindowName */, displayId);
-            return this;
-        }
-
-        /**
-         * Set focus on the window identified by the input {@code token} if the window identified by
-         * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not
-         * have focus, the request is dropped.
-         *
-         * This is used by forward focus transfer requests from clients that host embedded windows,
-         * and want to transfer focus to/from them.
-         *
-         * @hide
-         */
-        public Transaction requestFocusTransfer(@NonNull IBinder token,
-                                                String windowName,
-                                                @NonNull IBinder focusedToken,
-                                                String focusedWindowName,
-                                                int displayId) {
-            nativeSetFocusedWindow(mNativeObject, token, windowName, focusedToken,
-                    focusedWindowName, displayId);
+            nativeSetFocusedWindow(mNativeObject, token, windowName, displayId);
             return this;
         }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6cd8941..003307d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -92,6 +92,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.input.InputManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -5413,7 +5414,7 @@
     /**
      * The pointer icon when the mouse hovers on this view. The default is null.
      */
-    private PointerIcon mPointerIcon;
+    private PointerIcon mMousePointerIcon;
 
     /**
      * @hide
@@ -29506,30 +29507,71 @@
     }
 
     /**
-     * Returns the pointer icon for the motion event, or null if it doesn't specify the icon.
-     * The default implementation does not care the location or event types, but some subclasses
-     * may use it (such as WebViews).
-     * @param event The MotionEvent from a mouse
-     * @param pointerIndex The index of the pointer for which to retrieve the {@link PointerIcon}.
-     *                     This will be between 0 and {@link MotionEvent#getPointerCount()}.
+     * Resolve the pointer icon that should be used for specified pointer in the motion event.
+     *
+     * The default implementation will resolve the pointer icon to one set using
+     * {@link #setPointerIcon(PointerIcon)} for mouse devices. Subclasses may override this to
+     * customize the icon for the given pointer.
+     *
+     * For example, the pointer icon for a stylus pointer can be resolved in the following way:
+     * <code><pre>
+     * &#64;Override
+     * public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
+     *     final int toolType = event.getToolType(pointerIndex);
+     *     if (!event.isFromSource(InputDevice.SOURCE_MOUSE)
+     *             && event.isFromSource(InputDevice.SOURCE_STYLUS)
+     *             && (toolType == MotionEvent.TOOL_TYPE_STYLUS
+     *                     || toolType == MotionEvent.TOOL_TYPE_ERASER)) {
+     *         // Show this pointer icon only if this pointer is a stylus.
+     *         return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_WAIT);
+     *     }
+     *     // Use the default logic for determining the pointer icon for other non-stylus pointers,
+     *     // like for the mouse cursor.
+     *     return super.onResolvePointerIcon(event, pointerIndex);
+     * }
+     * </pre></code>
+     *
+     * @param event The {@link MotionEvent} that requires a pointer icon to be resolved for one of
+     *              pointers.
+     * @param pointerIndex The index of the pointer in {@code event} for which to retrieve the
+     *     {@link PointerIcon}. This will be between 0 and {@link MotionEvent#getPointerCount()}.
+     * @return the pointer icon to use for specified pointer, or {@code null} if a pointer icon
+     *     is not specified and the default icon should be used.
      * @see PointerIcon
+     * @see InputManager#isStylusPointerIconEnabled()
      */
     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
         final float x = event.getX(pointerIndex);
         final float y = event.getY(pointerIndex);
         if (isDraggingScrollBar() || isOnScrollbarThumb(x, y)) {
-            return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW);
+            // Use the default pointer icon.
+            return null;
         }
-        return mPointerIcon;
+
+        // Note: A drawing tablet will have both SOURCE_MOUSE and SOURCE_STYLUS, but it would use
+        // TOOL_TYPE_STYLUS. For now, treat drawing tablets the same way as a mouse or touchpad.
+        if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+            return mMousePointerIcon;
+        }
+
+        return null;
     }
 
     /**
-     * Set the pointer icon for the current view.
+     * Set the pointer icon to be used for a mouse pointer in the current view.
+     *
      * Passing {@code null} will restore the pointer icon to its default value.
+     * Note that setting the pointer icon using this method will only set it for events coming from
+     * a mouse device (i.e. with source {@link InputDevice#SOURCE_MOUSE}). To resolve
+     * the pointer icon for other device types like styluses, override
+     * {@link #onResolvePointerIcon(MotionEvent, int)}.
+     *
      * @param pointerIcon A PointerIcon instance which will be shown when the mouse hovers.
+     * @see #onResolvePointerIcon(MotionEvent, int)
+     * @see PointerIcon
      */
     public void setPointerIcon(PointerIcon pointerIcon) {
-        mPointerIcon = pointerIcon;
+        mMousePointerIcon = pointerIcon;
         if (mAttachInfo == null || mAttachInfo.mHandlingPointerEvent) {
             return;
         }
@@ -29540,11 +29582,13 @@
     }
 
     /**
-     * Gets the pointer icon for the current view.
+     * Gets the mouse pointer icon for the current view.
+     *
+     * @see #setPointerIcon(PointerIcon)
      */
     @InspectableProperty
     public PointerIcon getPointerIcon() {
-        return mPointerIcon;
+        return mMousePointerIcon;
     }
 
     /**
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0560caf..9868144 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -57,18 +57,16 @@
         SurfaceControl mLeash;
         Rect mFrame;
         Rect mAttachedFrame;
+        IBinder mFocusGrantToken;
 
-        State(SurfaceControl sc, WindowManager.LayoutParams p,
-                int displayId, IBinder inputChannelToken, IWindow client, SurfaceControl leash,
-                Rect frame, Rect attachedFrame) {
+        State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client,
+                SurfaceControl leash, Rect frame) {
             mSurfaceControl = sc;
             mParams.copyFrom(p);
             mDisplayId = displayId;
-            mInputChannelToken = inputChannelToken;
             mClient = client;
             mLeash = leash;
             mFrame = frame;
-            mAttachedFrame = attachedFrame;
         }
     };
 
@@ -182,45 +180,53 @@
                 .setParent(leash)
                 .build();
 
+        final State state = new State(sc, attrs, displayId, window, leash, /* frame= */ new Rect());
+        synchronized (this) {
+            State parentState = mStateForWindow.get(attrs.token);
+            if (parentState != null) {
+                state.mAttachedFrame = parentState.mFrame;
+            }
+
+            // Give the first window the mFocusGrantToken since that's the token the host can use
+            // to give focus to the embedded.
+            if (mStateForWindow.isEmpty()) {
+                state.mFocusGrantToken = mFocusGrantToken;
+            } else {
+                state.mFocusGrantToken = new Binder();
+            }
+
+            mStateForWindow.put(window.asBinder(), state);
+        }
+
+        if (state.mAttachedFrame == null) {
+            outAttachedFrame.set(0, 0, -1, -1);
+        } else {
+            outAttachedFrame.set(state.mAttachedFrame);
+        }
+        outSizeCompatScale[0] = 1f;
+
         if (((attrs.inputFeatures &
                 WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
             try {
                 if (mRealWm instanceof IWindowSession.Stub) {
                     mRealWm.grantInputChannel(displayId,
                             new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
-                            window, mHostInputToken,
-                            attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type,
-                            attrs.token, mFocusGrantToken, attrs.getTitle().toString(),
+                            window, mHostInputToken, attrs.flags, attrs.privateFlags,
+                            attrs.inputFeatures, attrs.type,
+                            attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(),
                             outInputChannel);
                 } else {
                     mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
                             attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
-                            mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
+                            state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
                 }
+                state.mInputChannelToken =
+                        outInputChannel != null ? outInputChannel.getToken() : null;
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to grant input to surface: ", e);
             }
         }
 
-        final State state = new State(sc, attrs, displayId,
-                outInputChannel != null ? outInputChannel.getToken() : null, window,
-                leash, /* frame= */ new Rect(), /* attachedFrame= */ null);
-        Rect parentFrame = null;
-        synchronized (this) {
-            State parentState = mStateForWindow.get(attrs.token);
-            if (parentState != null) {
-                parentFrame = parentState.mFrame;
-            }
-            mStateForWindow.put(window.asBinder(), state);
-        }
-        state.mAttachedFrame = parentFrame;
-        if (parentFrame == null) {
-            outAttachedFrame.set(0, 0, -1, -1);
-        } else {
-            outAttachedFrame.set(parentFrame);
-        }
-        outSizeCompatScale[0] = 1f;
-
         final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
                         WindowManagerGlobal.ADD_FLAG_USE_BLAST;
 
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 6872536..1840bcb 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1217,9 +1217,11 @@
      * notify cursor/anchor locations.
      *
      * @param cursorUpdateMode any combination of update modes and filters:
-     * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and date filters:
+     * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and data filters:
      * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
-     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
+     * {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}.
      * Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
      * rejected and method will return {@code false}.
      * @return {@code true} if the request is scheduled. {@code false} to indicate that when the
@@ -1240,7 +1242,9 @@
      * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}
      * @param cursorUpdateFilter any combination of data filters:
      * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
-     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
+     * {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}.
      *
      * <p>Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
      * rejected and method will return {@code false}.</p>
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 515b95c..82cf073 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1648,6 +1648,7 @@
      *
      * @param userId user ID to query
      * @return {@link List} of {@link InputMethodInfo}.
+     * @see #getEnabledInputMethodSubtypeListAsUser(String, boolean, int)
      * @hide
      */
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -1676,6 +1677,27 @@
     }
 
     /**
+     * Returns a list of enabled input method subtypes for the specified input method info for the
+     * specified user.
+     *
+     * @param imeId IME ID to be queried about.
+     * @param allowsImplicitlyEnabledSubtypes {@code true} to include implicitly enabled subtypes.
+     * @param userId user ID to be queried about.
+     *               {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required if this is
+     *               different from the calling process user ID.
+     * @return {@link List} of {@link InputMethodSubtype}.
+     * @see #getEnabledInputMethodListAsUser(int)
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+    public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+            @NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+        return IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList(
+                Objects.requireNonNull(imeId), allowsImplicitlyEnabledSubtypes, userId);
+    }
+
+    /**
      * @deprecated Use {@link InputMethodService#showStatusIcon(int)} instead. This method was
      * intended for IME developers who should be accessing APIs through the service. APIs in this
      * class are intended for app developers interacting with the IME.
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index eb91d08..ec50c69 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -405,21 +405,15 @@
                     }
                     if (handler.getLooper().isCurrentThread()) {
                         servedView.onInputConnectionClosedInternal();
-                        final ViewRootImpl viewRoot = servedView.getViewRootImpl();
-                        if (viewRoot != null) {
-                            viewRoot.getHandwritingInitiator().onInputConnectionClosed(servedView);
-                        }
                     } else {
                         handler.post(servedView::onInputConnectionClosedInternal);
-                        handler.post(() -> {
-                            final ViewRootImpl viewRoot = servedView.getViewRootImpl();
-                            if (viewRoot != null) {
-                                viewRoot.getHandwritingInitiator()
-                                        .onInputConnectionClosed(servedView);
-                            }
-                        });
                     }
                 }
+
+                final ViewRootImpl viewRoot = servedView.getViewRootImpl();
+                if (viewRoot != null) {
+                    viewRoot.getHandwritingInitiator().onInputConnectionClosed(servedView);
+                }
             }
         });
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d56a06f..67c9f8c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6232,7 +6232,10 @@
      * @attr ref android.R.styleable#TextView_lineHeight
      */
     @android.view.RemotableViewMethod
-    public void setLineHeight(int unit, @FloatRange(from = 0) float lineHeight) {
+    public void setLineHeight(
+            @TypedValue.ComplexDimensionUnit int unit,
+            @FloatRange(from = 0) float lineHeight
+    ) {
         setLineHeightPx(
                 TypedValue.applyDimension(unit, lineHeight, getDisplayMetricsOrSystem()));
     }
diff --git a/core/java/android/window/RemoteTransition.java b/core/java/android/window/RemoteTransition.java
index 4bd15f2..4cc7ec5 100644
--- a/core/java/android/window/RemoteTransition.java
+++ b/core/java/android/window/RemoteTransition.java
@@ -38,9 +38,18 @@
     /** The application thread that will be running the remote transition. */
     private @Nullable IApplicationThread mAppThread;
 
+    /** A name for this that can be used for debugging. */
+    private @Nullable String mDebugName;
+
     /** Constructs with no app thread (animation runs in shell). */
     public RemoteTransition(@NonNull IRemoteTransition remoteTransition) {
-        this(remoteTransition, null /* appThread */);
+        this(remoteTransition, null /* appThread */, null /* debugName */);
+    }
+
+    /** Constructs with no app thread (animation runs in shell). */
+    public RemoteTransition(@NonNull IRemoteTransition remoteTransition,
+            @Nullable String debugName) {
+        this(remoteTransition, null /* appThread */, debugName);
     }
 
     /** Get the IBinder associated with the underlying IRemoteTransition. */
@@ -70,15 +79,19 @@
      *   The actual remote-transition interface used to run the transition animation.
      * @param appThread
      *   The application thread that will be running the remote transition.
+     * @param debugName
+     *   A name for this that can be used for debugging.
      */
     @DataClass.Generated.Member
     public RemoteTransition(
             @NonNull IRemoteTransition remoteTransition,
-            @Nullable IApplicationThread appThread) {
+            @Nullable IApplicationThread appThread,
+            @Nullable String debugName) {
         this.mRemoteTransition = remoteTransition;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mRemoteTransition);
         this.mAppThread = appThread;
+        this.mDebugName = debugName;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -100,6 +113,14 @@
     }
 
     /**
+     * A name for this that can be used for debugging.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getDebugName() {
+        return mDebugName;
+    }
+
+    /**
      * The actual remote-transition interface used to run the transition animation.
      */
     @DataClass.Generated.Member
@@ -119,6 +140,15 @@
         return this;
     }
 
+    /**
+     * A name for this that can be used for debugging.
+     */
+    @DataClass.Generated.Member
+    public @NonNull RemoteTransition setDebugName(@NonNull String value) {
+        mDebugName = value;
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -127,7 +157,8 @@
 
         return "RemoteTransition { " +
                 "remoteTransition = " + mRemoteTransition + ", " +
-                "appThread = " + mAppThread +
+                "appThread = " + mAppThread + ", " +
+                "debugName = " + mDebugName +
         " }";
     }
 
@@ -139,9 +170,11 @@
 
         byte flg = 0;
         if (mAppThread != null) flg |= 0x2;
+        if (mDebugName != null) flg |= 0x4;
         dest.writeByte(flg);
         dest.writeStrongInterface(mRemoteTransition);
         if (mAppThread != null) dest.writeStrongInterface(mAppThread);
+        if (mDebugName != null) dest.writeString(mDebugName);
     }
 
     @Override
@@ -158,11 +191,13 @@
         byte flg = in.readByte();
         IRemoteTransition remoteTransition = IRemoteTransition.Stub.asInterface(in.readStrongBinder());
         IApplicationThread appThread = (flg & 0x2) == 0 ? null : IApplicationThread.Stub.asInterface(in.readStrongBinder());
+        String debugName = (flg & 0x4) == 0 ? null : in.readString();
 
         this.mRemoteTransition = remoteTransition;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mRemoteTransition);
         this.mAppThread = appThread;
+        this.mDebugName = debugName;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -182,10 +217,10 @@
     };
 
     @DataClass.Generated(
-            time = 1630690027011L,
+            time = 1678926409863L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/window/RemoteTransition.java",
-            inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+            inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\nprivate @android.annotation.Nullable java.lang.String mDebugName\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 4c482460..0f3eef7 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -149,8 +149,11 @@
     /** The task is launching behind home. */
     public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19;
 
+    /** The task became the top-most task even if it didn't change visibility. */
+    public static final int FLAG_MOVED_TO_TOP = 1 << 20;
+
     /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
-    public static final int FLAG_FIRST_CUSTOM = 1 << 20;
+    public static final int FLAG_FIRST_CUSTOM = 1 << 21;
 
     /** The change belongs to a window that won't contain activities. */
     public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -179,6 +182,7 @@
             FLAG_BACK_GESTURE_ANIMATED,
             FLAG_NO_ANIMATION,
             FLAG_TASK_LAUNCHING_BEHIND,
+            FLAG_MOVED_TO_TOP,
             FLAG_FIRST_CUSTOM
     })
     public @interface ChangeFlags {}
@@ -190,6 +194,9 @@
 
     private AnimationOptions mOptions;
 
+    /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
+    private int mDebugId = -1;
+
     /** @hide */
     public TransitionInfo(@TransitionType int type, @TransitionFlags int flags) {
         mType = type;
@@ -202,6 +209,7 @@
         in.readTypedList(mChanges, Change.CREATOR);
         in.readTypedList(mRoots, Root.CREATOR);
         mOptions = in.readTypedObject(AnimationOptions.CREATOR);
+        mDebugId = in.readInt();
     }
 
     @Override
@@ -212,6 +220,7 @@
         dest.writeTypedList(mChanges);
         dest.writeTypedList(mRoots, flags);
         dest.writeTypedObject(mOptions, flags);
+        dest.writeInt(mDebugId);
     }
 
     @NonNull
@@ -347,11 +356,24 @@
         return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
     }
 
+    /**
+     * Set an arbitrary "debug" id for this info. This id will not be used for any "real work",
+     * it is just for debugging and logging.
+     */
+    public void setDebugId(int id) {
+        mDebugId = id;
+    }
+
+    /** Get the "debug" id of this info. Do NOT use this for real work, only use for debugging. */
+    public int getDebugId() {
+        return mDebugId;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("{t=").append(transitTypeToString(mType)).append(" f=0x")
-                .append(Integer.toHexString(mFlags)).append(" r=[");
+        sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
+                .append(" f=0x").append(Integer.toHexString(mFlags)).append(" r=[");
         for (int i = 0; i < mRoots.size(); ++i) {
             if (i > 0) {
                 sb.append(',');
@@ -510,6 +532,7 @@
      */
     public TransitionInfo localRemoteCopy() {
         final TransitionInfo out = new TransitionInfo(mType, mFlags);
+        out.mDebugId = mDebugId;
         for (int i = 0; i < mChanges.size(); ++i) {
             out.mChanges.add(mChanges.get(i).localRemoteCopy());
         }
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ad20432..9f804b1 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -1586,61 +1586,109 @@
             return mShortcutInfo;
         }
 
+        /** Gets a string representation of a hierarchy-op type. */
+        public static String hopToString(int type) {
+            switch (type) {
+                case HIERARCHY_OP_TYPE_REPARENT: return "reparent";
+                case HIERARCHY_OP_TYPE_REORDER: return "reorder";
+                case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "ChildrenTasksReparent";
+                case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "SetLaunchRoot";
+                case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
+                case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
+                case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+                case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
+                case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
+                case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
+                case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
+                    return "removeInsetsFrameProvider";
+                case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop";
+                case HIERARCHY_OP_TYPE_REMOVE_TASK: return "RemoveTask";
+                case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity";
+                case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "ClearAdjacentRoot";
+                case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
+                    return "setReparentLeafTaskIfRelaunch";
+                case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
+                    return "addTaskFragmentOperation";
+                default: return "HOP(" + type + ")";
+            }
+        }
+
         @Override
         public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("{").append(hopToString(mType)).append(": ");
             switch (mType) {
                 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
-                    return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
-                            + " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly
-                            + " mWindowingMode=" + Arrays.toString(mWindowingModes)
-                            + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+                    sb.append("from=").append(mContainer).append(" to=").append(mReparent)
+                            .append(" mToTop=").append(mToTop)
+                            .append(" mReparentTopOnly=").append(mReparentTopOnly)
+                            .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+                            .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
+                    break;
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
-                    return "{SetLaunchRoot: container=" + mContainer
-                            + " mWindowingMode=" + Arrays.toString(mWindowingModes)
-                            + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+                            .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
+                    break;
                 case HIERARCHY_OP_TYPE_REPARENT:
-                    return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
-                            + mReparent + "}";
+                    sb.append(mContainer).append(" to ").append(mToTop ? "top of " : "bottom of ")
+                            .append(mReparent);
+                    break;
                 case HIERARCHY_OP_TYPE_REORDER:
-                    return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+                    sb.append(mContainer).append(" to ").append(mToTop ? "top" : "bottom");
+                    break;
                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
-                    return "{SetAdjacentRoot: container=" + mContainer
-                            + " adjacentRoot=" + mReparent + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" adjacentRoot=").append(mReparent);
+                    break;
                 case HIERARCHY_OP_TYPE_LAUNCH_TASK:
-                    return "{LaunchTask: " + mLaunchOptions + "}";
+                    sb.append(mLaunchOptions);
+                    break;
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
-                    return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
-                            + "}";
+                    sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop);
+                    break;
                 case HIERARCHY_OP_TYPE_START_SHORTCUT:
-                    return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
-                            + "}";
+                    sb.append("options=").append(mLaunchOptions)
+                            .append(" info=").append(mShortcutInfo);
+                    break;
+                case HIERARCHY_OP_TYPE_PENDING_INTENT:
+                    sb.append("options=").append(mLaunchOptions);
+                    break;
                 case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER:
-                    return "{addRectInsetsProvider: container=" + mContainer
-                            + " provider=" + mInsetsFrameProvider + "}";
                 case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
-                    return "{removeLocalInsetsProvider: container=" + mContainer
-                            + " provider=" + mInsetsFrameProvider + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" provider=").append(mInsetsFrameProvider);
+                    break;
                 case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
-                    return "{setAlwaysOnTop: container=" + mContainer
-                            + " alwaysOnTop=" + mAlwaysOnTop + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" alwaysOnTop=").append(mAlwaysOnTop);
+                    break;
                 case HIERARCHY_OP_TYPE_REMOVE_TASK:
-                    return "{RemoveTask: task=" + mContainer + "}";
+                    sb.append("task=").append(mContainer);
+                    break;
                 case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
-                    return "{finishActivity: activity=" + mContainer + "}";
+                    sb.append("activity=").append(mContainer);
+                    break;
                 case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS:
-                    return "{ClearAdjacentRoot: container=" + mContainer + "}";
+                    sb.append("container=").append(mContainer);
+                    break;
                 case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
-                    return "{setReparentLeafTaskIfRelaunch: container= " + mContainer
-                            + " reparentLeafTaskIfRelaunch= " + mReparentLeafTaskIfRelaunch + "}";
+                    sb.append("container= ").append(mContainer)
+                            .append(" reparentLeafTaskIfRelaunch= ")
+                            .append(mReparentLeafTaskIfRelaunch);
+                    break;
                 case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
-                    return "{addTaskFragmentOperation: fragmentToken= " + mContainer
-                            + " operation= " + mTaskFragmentOperation + "}";
+                    sb.append("fragmentToken= ").append(mContainer)
+                            .append(" operation= ").append(mTaskFragmentOperation);
+                    break;
                 default:
-                    return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
-                            + " mToTop=" + mToTop
-                            + " mWindowingMode=" + Arrays.toString(mWindowingModes)
-                            + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" reparent=").append(mReparent)
+                            .append(" mToTop=").append(mToTop)
+                            .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+                            .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
             }
+            return sb.append("}").toString();
         }
 
         @Override
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 0cb87fe..7ad2a68 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -524,11 +524,6 @@
     public static final String DEFAULT_QR_CODE_SCANNER = "default_qr_code_scanner";
 
     /**
-     * (boolean) Whether the task manager entrypoint is enabled.
-     */
-    public static final String TASK_MANAGER_ENABLED = "task_manager_enabled";
-
-    /**
      * (boolean) Whether the task manager should show an attention grabbing dot when tasks changed.
      */
     public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot";
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f7c03cd..ae58626 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -28,6 +28,7 @@
 import android.media.MediaRoute2Info;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.IAddTileResultCallback;
@@ -141,7 +142,7 @@
     void addQsTile(in ComponentName tile);
     void remQsTile(in ComponentName tile);
     void clickQsTile(in ComponentName tile);
-    void handleSystemKey(in int key);
+    void handleSystemKey(in KeyEvent key);
 
     /**
      * Methods to show toast messages for screen pinning
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index c1dbc87..3708859 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.view.KeyEvent;
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.logging.InstanceId;
@@ -110,7 +111,7 @@
     void remTile(in ComponentName tile);
     void clickTile(in ComponentName tile);
     @UnsupportedAppUsage
-    void handleSystemKey(in int key);
+    void handleSystemKey(in KeyEvent key);
     int getLastSystemKey();
 
     /**
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 241320f..416d991 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -74,6 +74,7 @@
     WeakRefHandleField touchableRegionSurfaceControl;
     jfieldID transform;
     jfieldID windowToken;
+    jfieldID focusTransferTarget;
 } gInputWindowHandleClassInfo;
 
 static struct {
@@ -216,6 +217,17 @@
         mInfo.windowToken.clear();
     }
 
+    ScopedLocalRef<jobject>
+            focusTransferTargetObj(env,
+                                   env->GetObjectField(obj,
+                                                       gInputWindowHandleClassInfo
+                                                               .focusTransferTarget));
+    if (focusTransferTargetObj.get()) {
+        mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
+    } else {
+        mInfo.focusTransferTarget.clear();
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -433,6 +445,9 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
                  "Landroid/os/IBinder;");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.focusTransferTarget, clazz, "focusTransferTarget",
+                 "Landroid/os/IBinder;");
+
     jclass weakRefClazz;
     FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 03d6eec..e42c6f1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1820,17 +1820,11 @@
 }
 
 static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
-                                   jobject toTokenObj, jstring windowNameJstr,
-                                   jobject focusedTokenObj, jstring focusedWindowNameJstr,
-                                   jint displayId) {
+                                   jobject toTokenObj, jstring windowNameJstr, jint displayId) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     if (toTokenObj == NULL) return;
 
     sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
-    sp<IBinder> focusedToken;
-    if (focusedTokenObj != NULL) {
-        focusedToken = ibinderForJavaObject(env, focusedTokenObj);
-    }
 
     FocusRequest request;
     request.token = toToken;
@@ -1839,11 +1833,6 @@
         request.windowName = windowName.c_str();
     }
 
-    request.focusedToken = focusedToken;
-    if (focusedWindowNameJstr != NULL) {
-        ScopedUtfChars focusedWindowName(env, focusedWindowNameJstr);
-        request.focusedWindowName = focusedWindowName.c_str();
-    }
     request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
     request.displayId = displayId;
     transaction->setFocusedWindow(request);
@@ -2236,7 +2225,7 @@
             (void*)nativeGetHandle },
     {"nativeSetFixedTransformHint", "(JJI)V",
             (void*)nativeSetFixedTransformHint},
-    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V",
+    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;I)V",
             (void*)nativeSetFocusedWindow},
     {"nativeRemoveCurrentInputFocus", "(JI)V",
             (void*)nativeRemoveCurrentInputFocus},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index e6c8557..bb3089b 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -391,6 +391,9 @@
     optional int32 last_drop_input_mode = 36;
     optional int32 override_orientation = 37 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
     optional bool should_send_compat_fake_focus = 38;
+    optional bool should_force_rotate_for_camera_compat = 39;
+    optional bool should_refresh_activity_for_camera_compat = 40;
+    optional bool should_refresh_activity_via_pause_for_camera_compat = 41;
 }
 
 /* represents WindowToken */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 774899d..ef19fc1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6379,4 +6379,6 @@
     <!-- Whether we should persist the brightness value in nits for the default display even if
          the underlying display device changes. -->
     <bool name="config_persistBrightnessNitsForDefaultDisplay">false</bool>
+    <!-- Whether to request the approval before commit sessions. -->
+    <bool name="config_isPreApprovalRequestAvailable">true</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 79f3dcd..8855d5b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2238,6 +2238,7 @@
   <java-symbol type="array" name="config_nonPreemptibleInputMethods" />
   <java-symbol type="bool" name="config_enhancedConfirmationModeEnabled" />
   <java-symbol type="bool" name="config_persistBrightnessNitsForDefaultDisplay" />
+  <java-symbol type="bool" name="config_isPreApprovalRequestAvailable" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 2e3d145..a64bb21 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -250,16 +250,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -282,7 +282,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -345,16 +344,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -377,7 +376,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -439,16 +437,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -471,7 +469,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -535,16 +532,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -567,7 +564,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -630,16 +626,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -662,7 +658,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -733,16 +728,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -765,7 +760,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -827,16 +821,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -859,7 +853,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -920,16 +913,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -952,7 +945,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1014,16 +1006,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1046,7 +1038,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1124,16 +1115,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1156,7 +1147,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1219,16 +1209,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1251,7 +1241,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1312,16 +1301,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1344,7 +1333,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1407,16 +1395,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1439,7 +1427,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1501,16 +1488,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1533,7 +1520,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1595,16 +1581,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1627,7 +1613,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1689,16 +1674,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1721,7 +1706,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1783,16 +1767,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1815,7 +1799,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -1877,16 +1860,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1909,7 +1892,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -1976,16 +1958,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2008,7 +1990,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2068,16 +2049,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2100,7 +2081,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2298,16 +2278,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2330,8 +2310,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2393,16 +2371,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2425,8 +2403,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2487,16 +2463,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2519,8 +2495,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -2582,16 +2556,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2614,8 +2588,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -2679,16 +2651,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2711,8 +2683,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -2775,16 +2745,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2807,8 +2777,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -2877,16 +2845,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2909,8 +2877,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -2975,16 +2941,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3007,11 +2973,9 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
-     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
+    <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
     <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
         <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
         <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -3072,16 +3036,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3104,8 +3068,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3170,16 +3132,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3202,8 +3164,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3249,16 +3209,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3281,8 +3241,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -3328,16 +3286,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3360,8 +3318,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -3426,16 +3382,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3458,8 +3414,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -3525,16 +3479,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3557,8 +3511,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -3622,16 +3574,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3654,8 +3606,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -3718,16 +3668,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3750,8 +3700,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -3813,16 +3761,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3845,8 +3793,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -3908,16 +3854,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3940,8 +3886,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4001,16 +3945,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4033,8 +3977,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -4347,16 +4289,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4379,7 +4321,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -4543,16 +4484,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4575,7 +4516,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4639,16 +4579,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4671,7 +4611,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4813,16 +4752,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4845,8 +4784,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -4870,16 +4807,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4902,7 +4839,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -4922,16 +4858,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4954,8 +4890,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -4986,16 +4920,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -5018,7 +4952,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
     <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
         <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bf8ca8b..4cccf8e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -76,7 +76,8 @@
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" />
+    <uses-permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java b/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
index 306ecde..61e976b 100644
--- a/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
+++ b/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
@@ -20,8 +20,6 @@
 import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
 import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import android.os.ConditionVariable;
 import android.provider.DeviceConfig;
 import android.util.Log;
@@ -33,7 +31,6 @@
 
 import com.google.common.collect.ImmutableMap;
 
-import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -46,7 +43,6 @@
 public final class FakeLatencyTracker extends LatencyTracker {
 
     private static final String TAG = "FakeLatencyTracker";
-    private static final Duration FORCE_UPDATE_TIMEOUT = Duration.ofSeconds(1);
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
@@ -203,7 +199,7 @@
             }
         }
         Log.i(TAG, "waiting for condition");
-        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+        mDeviceConfigPropertiesUpdated.block();
     }
 
     public void waitForMatchingActionProperties(ActionProperties actionProperties)
@@ -232,7 +228,7 @@
             }
         }
         Log.i(TAG, "waiting for condition");
-        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+        mDeviceConfigPropertiesUpdated.block();
     }
 
     public void waitForActionEnabledState(int action, boolean enabledState) throws Exception {
@@ -260,7 +256,7 @@
             }
         }
         Log.i(TAG, "waiting for condition");
-        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+        mDeviceConfigPropertiesUpdated.block();
     }
 
     public void waitForGlobalEnabledState(boolean enabledState) throws Exception {
@@ -280,6 +276,6 @@
             }
         }
         Log.i(TAG, "waiting for condition");
-        assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+        mDeviceConfigPropertiesUpdated.block();
     }
 }
diff --git a/graphics/java/android/graphics/HardwareBufferRenderer.java b/graphics/java/android/graphics/HardwareBufferRenderer.java
index 361dc59..e04f13c 100644
--- a/graphics/java/android/graphics/HardwareBufferRenderer.java
+++ b/graphics/java/android/graphics/HardwareBufferRenderer.java
@@ -275,11 +275,22 @@
             Consumer<RenderResult> wrapped = consumable -> executor.execute(
                     () -> renderCallback.accept(consumable));
             if (!isClosed()) {
+                int renderWidth;
+                int renderHeight;
+                if (mTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_90
+                        || mTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_270) {
+                    renderWidth = mHardwareBuffer.getHeight();
+                    renderHeight = mHardwareBuffer.getWidth();
+                } else {
+                    renderWidth = mHardwareBuffer.getWidth();
+                    renderHeight = mHardwareBuffer.getHeight();
+                }
+
                 nRender(
                         mProxy,
                         mTransform,
-                        mHardwareBuffer.getWidth(),
-                        mHardwareBuffer.getHeight(),
+                        renderWidth,
+                        renderHeight,
                         mColorSpace.getNativeInstance(),
                         wrapped);
             } else {
diff --git a/libs/WindowManager/Shell/res/drawable/caption_close_button.xml b/libs/WindowManager/Shell/res/drawable/caption_close_button.xml
deleted file mode 100644
index e258564..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_close_button.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateY="4.0">
-        <path
-            android:fillColor="#FFFF0000"
-            android:pathData="M12.45,38.35 L9.65,35.55 21.2,24 9.65,12.45 12.45,9.65 24,21.2 35.55,9.65 38.35,12.45 26.8,24 38.35,35.55 35.55,38.35 24,26.8Z"/>
-    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_desktop_button.xml b/libs/WindowManager/Shell/res/drawable/caption_desktop_button.xml
deleted file mode 100644
index 8779cc0..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_desktop_button.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateX="6.0"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M5.958,37.708Q4.458,37.708 3.354,36.604Q2.25,35.5 2.25,34V18.292Q2.25,16.792 3.354,15.688Q4.458,14.583 5.958,14.583H9.5V5.958Q9.5,4.458 10.625,3.354Q11.75,2.25 13.208,2.25H34Q35.542,2.25 36.646,3.354Q37.75,4.458 37.75,5.958V21.667Q37.75,23.167 36.646,24.271Q35.542,25.375 34,25.375H30.5V34Q30.5,35.5 29.396,36.604Q28.292,37.708 26.792,37.708ZM5.958,34H26.792Q26.792,34 26.792,34Q26.792,34 26.792,34V21.542H5.958V34Q5.958,34 5.958,34Q5.958,34 5.958,34ZM30.5,21.667H34Q34,21.667 34,21.667Q34,21.667 34,21.667V9.208H13.208V14.583H26.833Q28.375,14.583 29.438,15.667Q30.5,16.75 30.5,18.25Z"/>
-    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_floating_button.xml b/libs/WindowManager/Shell/res/drawable/caption_floating_button.xml
deleted file mode 100644
index ea0fbb0..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_floating_button.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateX="6.0"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M18.167,21.875H29.833V10.208H18.167ZM7.875,35.833Q6.375,35.833 5.271,34.729Q4.167,33.625 4.167,32.125V7.875Q4.167,6.375 5.271,5.271Q6.375,4.167 7.875,4.167H32.125Q33.625,4.167 34.729,5.271Q35.833,6.375 35.833,7.875V32.125Q35.833,33.625 34.729,34.729Q33.625,35.833 32.125,35.833ZM7.875,32.125H32.125Q32.125,32.125 32.125,32.125Q32.125,32.125 32.125,32.125V7.875Q32.125,7.875 32.125,7.875Q32.125,7.875 32.125,7.875H7.875Q7.875,7.875 7.875,7.875Q7.875,7.875 7.875,7.875V32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125ZM7.875,7.875Q7.875,7.875 7.875,7.875Q7.875,7.875 7.875,7.875V32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125V7.875Q7.875,7.875 7.875,7.875Q7.875,7.875 7.875,7.875Z"/>
-    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_fullscreen_button.xml b/libs/WindowManager/Shell/res/drawable/caption_fullscreen_button.xml
deleted file mode 100644
index c55cbe2..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_fullscreen_button.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateX="6.0"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M34.042,14.625V9.333Q34.042,9.333 34.042,9.333Q34.042,9.333 34.042,9.333H28.708V5.708H33.917Q35.458,5.708 36.562,6.833Q37.667,7.958 37.667,9.458V14.625ZM2.375,14.625V9.458Q2.375,7.958 3.479,6.833Q4.583,5.708 6.125,5.708H11.292V9.333H6Q6,9.333 6,9.333Q6,9.333 6,9.333V14.625ZM28.708,34.25V30.667H34.042Q34.042,30.667 34.042,30.667Q34.042,30.667 34.042,30.667V25.333H37.667V30.542Q37.667,32 36.562,33.125Q35.458,34.25 33.917,34.25ZM6.125,34.25Q4.583,34.25 3.479,33.125Q2.375,32 2.375,30.542V25.333H6V30.667Q6,30.667 6,30.667Q6,30.667 6,30.667H11.292V34.25ZM9.333,27.292V12.667H30.708V27.292ZM12.917,23.708H27.125V16.25H12.917ZM12.917,23.708V16.25V23.708Z"/>
-    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_more_button.xml b/libs/WindowManager/Shell/res/drawable/caption_more_button.xml
deleted file mode 100644
index 447df43..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_more_button.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateX="6.0"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M8.083,22.833Q6.917,22.833 6.104,22Q5.292,21.167 5.292,20Q5.292,18.833 6.125,18Q6.958,17.167 8.125,17.167Q9.292,17.167 10.125,18Q10.958,18.833 10.958,20Q10.958,21.167 10.125,22Q9.292,22.833 8.083,22.833ZM20,22.833Q18.833,22.833 18,22Q17.167,21.167 17.167,20Q17.167,18.833 18,18Q18.833,17.167 20,17.167Q21.167,17.167 22,18Q22.833,18.833 22.833,20Q22.833,21.167 22,22Q21.167,22.833 20,22.833ZM31.875,22.833Q30.708,22.833 29.875,22Q29.042,21.167 29.042,20Q29.042,18.833 29.875,18Q30.708,17.167 31.917,17.167Q33.083,17.167 33.896,18Q34.708,18.833 34.708,20Q34.708,21.167 33.875,22Q33.042,22.833 31.875,22.833Z"/>
-    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_select_button.xml b/libs/WindowManager/Shell/res/drawable/caption_select_button.xml
deleted file mode 100644
index 8c60c84..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_select_button.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group
-           android:translateX="4.0"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M13.7021 12.5833L16.5676 15.5L15.426 16.7333L12.526 13.8333L10.4426 15.9167V10.5H15.9176L13.7021 12.5833ZM13.8343 3.83333H15.501V5.5H13.8343V3.83333ZM15.501 2.16667H13.8343V0.566667C14.751 0.566667 15.501 1.33333 15.501 2.16667ZM10.501 0.5H12.1676V2.16667H10.501V0.5ZM13.8343 7.16667H15.501V8.83333H13.8343V7.16667ZM5.50098 15.5H3.83431V13.8333H5.50098V15.5ZM2.16764 5.5H0.500977V3.83333H2.16764V5.5ZM2.16764 0.566667V2.16667H0.500977C0.500977 1.33333 1.33431 0.566667 2.16764 0.566667ZM2.16764 12.1667H0.500977V10.5H2.16764V12.1667ZM5.50098 2.16667H3.83431V0.5H5.50098V2.16667ZM8.83431 2.16667H7.16764V0.5H8.83431V2.16667ZM8.83431 15.5H7.16764V13.8333H8.83431V15.5ZM2.16764 8.83333H0.500977V7.16667H2.16764V8.83333ZM2.16764 15.5667C1.25098 15.5667 0.500977 14.6667 0.500977 13.8333H2.16764V15.5667Z"/>
-    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_split_screen_button.xml b/libs/WindowManager/Shell/res/drawable/caption_split_screen_button.xml
deleted file mode 100644
index c334a54..0000000
--- a/libs/WindowManager/Shell/res/drawable/caption_split_screen_button.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:translateX="6.0"
-           android:translateY="8.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M18 14L13 14L13 2L18 2L18 14ZM20 14L20 2C20 0.9 19.1 -3.93402e-08 18 -8.74228e-08L13 -3.0598e-07C11.9 -3.54062e-07 11 0.9 11 2L11 14C11 15.1 11.9 16 13 16L18 16C19.1 16 20 15.1 20 14ZM7 14L2 14L2 2L7 2L7 14ZM9 14L9 2C9 0.9 8.1 -5.20166e-07 7 -5.68248e-07L2 -7.86805e-07C0.9 -8.34888e-07 -3.93403e-08 0.9 -8.74228e-08 2L-6.11959e-07 14C-6.60042e-07 15.1 0.9 16 2 16L7 16C8.1 16 9 15.1 9 14Z"/>    </group>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
index c6e634c..4ee10f4 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
@@ -17,6 +17,5 @@
 <shape android:shape="rectangle"
        xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="@android:color/white" />
-    <corners android:radius="@dimen/caption_menu_corner_radius" />
-    <stroke android:width="1dp" android:color="#b3b3b3"/>
+    <corners android:radius="@dimen/desktop_mode_handle_menu_corner_radius" />
 </shape>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_close.xml
similarity index 62%
copy from libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
copy to libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_close.xml
index 166552d..b7521d4 100644
--- a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_close.xml
@@ -15,16 +15,12 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
->
-    <group android:scaleX="1.25"
-           android:scaleY="1.75"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M10.3937 6.93935L11.3337 5.99935L6.00033 0.666016L0.666992 5.99935L1.60699 6.93935L6.00033 2.55268"/>
-    </group>
+    android:height="20dp"
+    android:tint="#000000"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="20dp">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_desktop.xml
similarity index 60%
copy from libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml
copy to libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_desktop.xml
index 7c86888..e2b724b 100644
--- a/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_desktop.xml
@@ -15,16 +15,12 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateY="4.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M10,38V28.35H13V35H19.65V38ZM10,19.65V10H19.65V13H13V19.65ZM28.35,38V35H35V28.35H38V38ZM35,19.65V13H28.35V10H38V19.65Z"/>
-    </group>
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M16.667,15H3.333V5H16.667V15ZM16.667,16.667C17.583,16.667 18.333,15.917 18.333,15V5C18.333,4.083 17.583,3.333 16.667,3.333H3.333C2.417,3.333 1.667,4.083 1.667,5V15C1.667,15.917 2.417,16.667 3.333,16.667H16.667ZM15,6.667H9.167V8.333H13.333V10H15V6.667ZM5,9.167H12.5V13.333H5V9.167Z"
+      android:fillColor="#1C1C14"
+      android:fillType="evenOdd"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_floating.xml
similarity index 62%
copy from libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
copy to libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_floating.xml
index 166552d..b0ea98e 100644
--- a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_floating.xml
@@ -15,16 +15,12 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
->
-    <group android:scaleX="1.25"
-           android:scaleY="1.75"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M10.3937 6.93935L11.3337 5.99935L6.00033 0.666016L0.666992 5.99935L1.60699 6.93935L6.00033 2.55268"/>
-    </group>
+    android:width="21dp"
+    android:height="20dp"
+    android:viewportWidth="21"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M3.667,15H17V5H3.667V15ZM18.667,15C18.667,15.917 17.917,16.667 17,16.667H3.667C2.75,16.667 2,15.917 2,15V5C2,4.083 2.75,3.333 3.667,3.333H17C17.917,3.333 18.667,4.083 18.667,5V15ZM11.167,6.667H15.333V11.667H11.167V6.667Z"
+      android:fillColor="#1C1C14"
+      android:fillType="evenOdd"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_fullscreen.xml
similarity index 62%
rename from libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
rename to libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_fullscreen.xml
index 166552d..99e1d26 100644
--- a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_fullscreen.xml
@@ -15,16 +15,12 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
->
-    <group android:scaleX="1.25"
-           android:scaleY="1.75"
-           android:translateY="6.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M10.3937 6.93935L11.3337 5.99935L6.00033 0.666016L0.666992 5.99935L1.60699 6.93935L6.00033 2.55268"/>
-    </group>
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M3.333,15H16.667V5H3.333V15ZM18.333,15C18.333,15.917 17.583,16.667 16.667,16.667H3.333C2.417,16.667 1.667,15.917 1.667,15V5C1.667,4.083 2.417,3.333 3.333,3.333H16.667C17.583,3.333 18.333,4.083 18.333,5V15ZM5,6.667H15V13.333H5V6.667Z"
+      android:fillColor="#1C1C14"
+      android:fillType="evenOdd"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_screenshot.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_screenshot.xml
new file mode 100644
index 0000000..79a9125
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_screenshot.xml
@@ -0,0 +1,34 @@
+<?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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M18.333,5.833L18.333,8.333L16.667,8.333L16.667,5.833L13.333,5.833L13.333,4.167L16.667,4.167C17.587,4.167 18.333,4.913 18.333,5.833Z"
+      android:fillColor="#1C1C14"/>
+  <path
+      android:pathData="M6.667,4.167L3.333,4.167C2.413,4.167 1.667,4.913 1.667,5.833L1.667,8.333L3.333,8.333L3.333,5.833L6.667,5.833L6.667,4.167Z"
+      android:fillColor="#1C1C14"/>
+  <path
+      android:pathData="M6.667,14.167L3.333,14.167L3.333,11.667L1.667,11.667L1.667,14.167C1.667,15.087 2.413,15.833 3.333,15.833L6.667,15.833L6.667,14.167Z"
+      android:fillColor="#1C1C14"/>
+  <path
+      android:pathData="M13.333,15.833L16.667,15.833C17.587,15.833 18.333,15.087 18.333,14.167L18.333,11.667L16.667,11.667L16.667,14.167L13.333,14.167L13.333,15.833Z"
+      android:fillColor="#1C1C14"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_select.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_select.xml
new file mode 100644
index 0000000..7c4f499
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_select.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M15.701,14.583L18.567,17.5L17.425,18.733L14.525,15.833L12.442,17.917V12.5H17.917L15.701,14.583ZM15.833,5.833H17.5V7.5H15.833V5.833ZM17.5,4.167H15.833V2.567C16.75,2.567 17.5,3.333 17.5,4.167ZM12.5,2.5H14.167V4.167H12.5V2.5ZM15.833,9.167H17.5V10.833H15.833V9.167ZM7.5,17.5H5.833V15.833H7.5V17.5ZM4.167,7.5H2.5V5.833H4.167V7.5ZM4.167,2.567V4.167H2.5C2.5,3.333 3.333,2.567 4.167,2.567ZM4.167,14.167H2.5V12.5H4.167V14.167ZM7.5,4.167H5.833V2.5H7.5V4.167ZM10.833,4.167H9.167V2.5H10.833V4.167ZM10.833,17.5H9.167V15.833H10.833V17.5ZM4.167,10.833H2.5V9.167H4.167V10.833ZM4.167,17.567C3.25,17.567 2.5,16.667 2.5,15.833H4.167V17.567Z"
+      android:fillColor="#1C1C14"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_splitscreen.xml
similarity index 61%
rename from libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml
rename to libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_splitscreen.xml
index 7c86888..853ab60 100644
--- a/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_splitscreen.xml
@@ -15,16 +15,12 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
->
-    <group android:scaleX="0.5"
-           android:scaleY="0.5"
-           android:translateY="4.0">
-        <path
-            android:fillColor="@android:color/black"
-            android:pathData="M10,38V28.35H13V35H19.65V38ZM10,19.65V10H19.65V13H13V19.65ZM28.35,38V35H35V28.35H38V38ZM35,19.65V13H28.35V10H38V19.65Z"/>
-    </group>
+    android:width="21dp"
+    android:height="20dp"
+    android:viewportWidth="21"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M17.333,15H4V5H17.333V15ZM17.333,16.667C18.25,16.667 19,15.917 19,15V5C19,4.083 18.25,3.333 17.333,3.333H4C3.083,3.333 2.333,4.083 2.333,5V15C2.333,15.917 3.083,16.667 4,16.667H17.333ZM9.833,6.667H5.667V13.333H9.833V6.667ZM11.5,6.667H15.667V13.333H11.5V6.667Z"
+      android:fillColor="#1C1C14"
+      android:fillType="evenOdd"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 35562b6..f6b21ba 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -61,7 +61,7 @@
             android:layout_width="32dp"
             android:layout_height="32dp"
             android:padding="4dp"
-            android:contentDescription="@string/collapse_menu_text"
+            android:contentDescription="@string/expand_menu_text"
             android:src="@drawable/ic_baseline_expand_more_24"
             android:tint="@color/desktop_mode_caption_expand_button_dark"
             android:background="@null"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
deleted file mode 100644
index ac13eae..0000000
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/handle_menu"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="@drawable/desktop_mode_decor_menu_background"
-    android:divider="?android:attr/dividerHorizontal"
-    android:showDividers="middle"
-    android:dividerPadding="18dip">
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
-        <ImageView
-            android:id="@+id/application_icon"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:layout_margin="12dp"
-            android:contentDescription="@string/app_icon_text"
-            android:layout_alignParentStart="true"
-            android:layout_centerVertical="true"/>
-        <TextView
-            android:id="@+id/application_name"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_toEndOf="@+id/application_icon"
-            android:layout_toStartOf="@+id/collapse_menu_button"
-            android:textColor="#FF000000"
-            android:layout_centerVertical="true"/>
-        <Button
-            android:id="@+id/collapse_menu_button"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:layout_marginEnd="10dp"
-            android:contentDescription="@string/collapse_menu_text"
-            android:layout_alignParentEnd="true"
-            android:background="@drawable/ic_baseline_expand_more_24"
-            android:layout_centerVertical="true"/>
-    </RelativeLayout>
-    <LinearLayout
-        android:id="@+id/windowing_mode_buttons"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal">
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="0.5" />
-        <ImageButton
-            style="@style/CaptionWindowingButtonStyle"
-            android:id="@+id/fullscreen_button"
-            android:contentDescription="@string/fullscreen_text"
-            android:src="@drawable/caption_fullscreen_button"
-            android:scaleType="fitCenter"
-            android:background="?android:selectableItemBackgroundBorderless"/>
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="1" />
-        <ImageButton
-            style="@style/CaptionWindowingButtonStyle"
-            android:id="@+id/split_screen_button"
-            android:contentDescription="@string/split_screen_text"
-            android:src="@drawable/caption_split_screen_button"
-            android:scaleType="fitCenter"
-            android:background="?android:selectableItemBackgroundBorderless"/>
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="1" />
-        <ImageButton
-            style="@style/CaptionWindowingButtonStyle"
-            android:id="@+id/floating_button"
-            android:contentDescription="@string/float_button_text"
-            android:src="@drawable/caption_floating_button"
-            android:scaleType="fitCenter"
-            android:background="?android:selectableItemBackgroundBorderless"/>
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="1" />
-        <ImageButton
-            style="@style/CaptionWindowingButtonStyle"
-            android:id="@+id/desktop_button"
-            android:contentDescription="@string/desktop_text"
-            android:src="@drawable/caption_desktop_button"
-            android:scaleType="fitCenter"
-            android:background="?android:selectableItemBackgroundBorderless"/>
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="0.5" />
-
-    </LinearLayout>
-    <LinearLayout
-        android:id="@+id/menu_buttons_misc"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-        <Button
-            style="@style/CaptionMenuButtonStyle"
-            android:id="@+id/screenshot_button"
-            android:contentDescription="@string/screenshot_text"
-            android:text="@string/screenshot_text"
-            android:drawableStart="@drawable/caption_screenshot_button"/>
-        <Button
-            style="@style/CaptionMenuButtonStyle"
-            android:id="@+id/select_button"
-            android:contentDescription="@string/select_text"
-            android:text="@string/select_text"
-            android:drawableStart="@drawable/caption_select_button"/>
-        <Button
-            style="@style/CaptionMenuButtonStyle"
-            android:id="@+id/close_button"
-            android:contentDescription="@string/close_text"
-            android:text="@string/close_text"
-            android:drawableStart="@drawable/caption_close_button"
-            android:textColor="#FFFF0000"/>
-    </LinearLayout>
-</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index 5ab159c..1d6864c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -25,8 +25,9 @@
 
     <ImageButton
         android:id="@+id/caption_handle"
-        android:layout_width="128dp"
+        android:layout_width="176dp"
         android:layout_height="42dp"
+        android:paddingHorizontal="24dp"
         android:contentDescription="@string/handle_text"
         android:src="@drawable/decor_handle_dark"
         tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
new file mode 100644
index 0000000..167a003
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
@@ -0,0 +1,57 @@
+<?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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="@dimen/desktop_mode_handle_menu_width"
+    android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
+    android:orientation="horizontal"
+    android:background="@drawable/desktop_mode_decor_menu_background"
+    android:gravity="center_vertical">
+
+    <ImageView
+        android:id="@+id/application_icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_marginStart="14dp"
+        android:layout_marginEnd="14dp"
+        android:contentDescription="@string/app_icon_text"/>
+
+    <TextView
+        android:id="@+id/application_name"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        tools:text="Gmail"
+        android:textColor="@color/desktop_mode_caption_menu_text_color"
+        android:textSize="14sp"
+        android:textFontWeight="500"
+        android:lineHeight="20dp"
+        android:textStyle="normal"
+        android:layout_weight="1"/>
+
+    <ImageButton
+        android:id="@+id/collapse_menu_button"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:padding="4dp"
+        android:layout_marginEnd="14dp"
+        android:layout_marginStart="14dp"
+        android:contentDescription="@string/collapse_menu_text"
+        android:src="@drawable/ic_baseline_expand_more_24"
+        android:rotation="180"
+        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:background="?android:selectableItemBackgroundBorderless"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
new file mode 100644
index 0000000..40a4b53
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
@@ -0,0 +1,47 @@
+<?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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/desktop_mode_handle_menu_width"
+    android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
+    android:orientation="vertical"
+    android:background="@drawable/desktop_mode_decor_menu_background">
+
+    <Button
+        android:id="@+id/screenshot_button"
+        android:contentDescription="@string/screenshot_text"
+        android:text="@string/screenshot_text"
+        android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
+        android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        style="@style/DesktopModeHandleMenuActionButton"/>
+
+    <Button
+        android:id="@+id/select_button"
+        android:contentDescription="@string/select_text"
+        android:text="@string/select_text"
+        android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
+        android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        style="@style/DesktopModeHandleMenuActionButton"/>
+
+    <Button
+        android:id="@+id/close_button"
+        android:contentDescription="@string/close_text"
+        android:text="@string/close_text"
+        android:drawableStart="@drawable/desktop_mode_ic_handle_menu_close"
+        android:drawableTint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        style="@style/DesktopModeHandleMenuActionButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
new file mode 100644
index 0000000..95283b9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
@@ -0,0 +1,62 @@
+<?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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/desktop_mode_handle_menu_width"
+    android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
+    android:orientation="horizontal"
+    android:background="@drawable/desktop_mode_decor_menu_background"
+    android:gravity="center_vertical">
+
+    <ImageButton
+        android:id="@+id/fullscreen_button"
+        android:layout_marginEnd="4dp"
+        android:contentDescription="@string/fullscreen_text"
+        android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
+        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:layout_weight="1"
+        style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+    <ImageButton
+        android:id="@+id/split_screen_button"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp"
+        android:contentDescription="@string/split_screen_text"
+        android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
+        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:layout_weight="1"
+        style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+    <ImageButton
+        android:id="@+id/floating_button"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp"
+        android:contentDescription="@string/float_button_text"
+        android:src="@drawable/desktop_mode_ic_handle_menu_floating"
+        android:tint="@color/desktop_mode_caption_menu_buttons_color_inactive"
+        android:layout_weight="1"
+        style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+    <ImageButton
+        android:id="@+id/desktop_button"
+        android:layout_marginStart="4dp"
+        android:contentDescription="@string/desktop_text"
+        android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
+        android:tint="@color/desktop_mode_caption_menu_buttons_color_active"
+        android:layout_weight="1"
+        style="@style/DesktopModeHandleMenuWindowingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 4a1635d..4b885c2 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -67,4 +67,7 @@
     <color name="desktop_mode_caption_close_button_dark">#1C1C17</color>
     <color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
     <color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
+    <color name="desktop_mode_caption_menu_text_color">#191C1D</color>
+    <color name="desktop_mode_caption_menu_buttons_color_inactive">#191C1D</color>
+    <color name="desktop_mode_caption_menu_buttons_color_active">#00677E</color>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 3a8614a..9049ed5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -372,20 +372,34 @@
     <!-- Height of button (32dp)  + 2 * margin (5dp each). -->
     <dimen name="freeform_decor_caption_height">42dp</dimen>
 
-    <!-- Width of buttons (32dp each) + padding (128dp total). -->
-    <dimen name="freeform_decor_caption_menu_width">256dp</dimen>
+    <!-- The width of the handle menu in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_width">216dp</dimen>
 
-    <dimen name="freeform_decor_caption_menu_height">250dp</dimen>
-    <dimen name="freeform_decor_caption_menu_height_no_windowing_controls">210dp</dimen>
+    <!-- The height of the handle menu's "App Info" pill in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_app_info_pill_height">52dp</dimen>
+
+    <!-- The height of the handle menu's "Windowing" pill in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
+
+    <!-- The height of the handle menu's "More Actions" pill in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_more_actions_pill_height">156dp</dimen>
+
+    <!-- The top margin of the handle menu in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen>
+
+    <!-- The start margin of the handle menu in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_margin_start">6dp</dimen>
+
+    <!-- The margin between pills of the handle menu in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_pill_spacing_margin">2dp</dimen>
+
+    <!-- The radius of the caption menu corners. -->
+    <dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
+
+    <!-- The radius of the caption menu shadow. -->
+    <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
 
     <dimen name="freeform_resize_handle">30dp</dimen>
 
     <dimen name="freeform_resize_corner">44dp</dimen>
-
-    <!-- The radius of the caption menu shadow. -->
-    <dimen name="caption_menu_shadow_radius">4dp</dimen>
-
-    <!-- The radius of the caption menu corners. -->
-    <dimen name="caption_menu_corner_radius">20dp</dimen>
-
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 395fdd1..563fb4d 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -263,4 +263,6 @@
     <string name="close_text">Close</string>
     <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
     <string name="collapse_menu_text">Close Menu</string>
+    <!-- Accessibility text for the handle menu open menu button [CHAR LIMIT=NONE] -->
+    <string name="expand_menu_text">Open Menu</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index d0782ad..8cad385 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -30,6 +30,26 @@
         <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
     </style>
 
+    <style name="DesktopModeHandleMenuActionButton">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">52dp</item>
+        <item name="android:gravity">start|center_vertical</item>
+        <item name="android:padding">16dp</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textFontWeight">500</item>
+        <item name="android:textColor">@color/desktop_mode_caption_menu_text_color</item>
+        <item name="android:drawablePadding">16dp</item>
+        <item name="android:background">?android:selectableItemBackground</item>
+    </style>
+
+    <style name="DesktopModeHandleMenuWindowingButton">
+        <item name="android:layout_width">48dp</item>
+        <item name="android:layout_height">48dp</item>
+        <item name="android:padding">14dp</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:background">?android:selectableItemBackgroundBorderless</item>
+    </style>
+
     <style name="CaptionButtonStyle">
         <item name="android:layout_width">32dp</item>
         <item name="android:layout_height">32dp</item>
@@ -37,20 +57,6 @@
         <item name="android:padding">4dp</item>
     </style>
 
-    <style name="CaptionWindowingButtonStyle">
-        <item name="android:layout_width">40dp</item>
-        <item name="android:layout_height">40dp</item>
-        <item name="android:padding">4dp</item>
-    </style>
-
-    <style name="CaptionMenuButtonStyle" parent="@style/Widget.AppCompat.Button.Borderless">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">52dp</item>
-        <item name="android:layout_marginStart">10dp</item>
-        <item name="android:padding">4dp</item>
-        <item name="android:gravity">start|center_vertical</item>
-    </style>
-
     <style name="DockedDividerBackground">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">@dimen/split_divider_bar_width</item>
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 5459094..9eba5ec 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
@@ -1120,7 +1120,7 @@
             setDividerInteractive(!mImeShown || !mHasImeFocus || isFloating, true,
                     "onImeStartPositioning");
 
-            return needOffset ? IME_ANIMATION_NO_ALPHA : 0;
+            return mTargetYOffset != mLastYOffset ? IME_ANIMATION_NO_ALPHA : 0;
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e2cd7a0..d8e2f5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -54,6 +54,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.freeform.FreeformComponents;
 import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -677,13 +678,15 @@
             SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             Transitions transitions,
-            EnterDesktopTaskTransitionHandler transitionHandler,
+            EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
+            ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
             @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         return new DesktopTasksController(context, shellInit, shellController, displayController,
                 shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions,
-                transitionHandler, desktopModeTaskRepository, mainExecutor);
+                enterDesktopTransitionHandler, exitDesktopTransitionHandler,
+                desktopModeTaskRepository, mainExecutor);
     }
 
     @WMSingleton
@@ -695,6 +698,15 @@
 
     @WMSingleton
     @Provides
+    static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
+            Transitions transitions,
+            Context context
+    ) {
+        return new ExitDesktopTaskTransitionHandler(transitions, context);
+    }
+
+    @WMSingleton
+    @Provides
     @DynamicOverride
     static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
         return new DesktopModeTaskRepository();
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 cb04a43..c35cd5a 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
@@ -68,7 +68,8 @@
         private val syncQueue: SyncTransactionQueue,
         private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
         private val transitions: Transitions,
-        private val animationTransitionHandler: EnterDesktopTaskTransitionHandler,
+        private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
+        private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
         private val desktopModeTaskRepository: DesktopModeTaskRepository,
         @ShellMainThread private val mainExecutor: ShellExecutor
 ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
@@ -149,7 +150,7 @@
         wct.setBounds(taskInfo.token, startBounds)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            animationTransitionHandler.startTransition(
+            enterDesktopTaskTransitionHandler.startTransition(
                     Transitions.TRANSIT_ENTER_FREEFORM, wct)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
@@ -167,7 +168,8 @@
         wct.setBounds(taskInfo.token, freeformBounds)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            animationTransitionHandler.startTransition(Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct)
+            enterDesktopTaskTransitionHandler.startTransition(
+                Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
         }
@@ -191,6 +193,18 @@
         }
     }
 
+    fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) {
+        val wct = WindowContainerTransaction()
+        addMoveToFullscreenChanges(wct, task.token)
+
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            exitDesktopTaskTransitionHandler.startTransition(
+            Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct)
+        } else {
+            shellTaskOrganizer.applyTransaction(wct)
+        }
+    }
+
     /** Move a task to the front **/
     fun moveTaskToFront(taskInfo: ActivityManager.RunningTaskInfo) {
         val wct = WindowContainerTransaction()
@@ -396,9 +410,9 @@
         val statusBarHeight = displayController
                 .getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
         if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
-            moveToFullscreen(taskInfo.taskId)
             visualIndicator?.releaseFullscreenIndicator()
             visualIndicator = null
+            moveToFullscreenWithAnimation(taskInfo)
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
new file mode 100644
index 0000000..d18e98a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+
+/**
+ * The {@link Transitions.TransitionHandler} that handles transitions for desktop mode tasks
+ * entering and exiting freeform.
+ */
+public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
+    private static final int FULLSCREEN_ANIMATION_DURATION = 336;
+    private final Context mContext;
+    private final Transitions mTransitions;
+    private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
+
+    private Supplier<SurfaceControl.Transaction> mTransactionSupplier;
+
+    public ExitDesktopTaskTransitionHandler(
+            Transitions transitions,
+            Context context) {
+        this(transitions, SurfaceControl.Transaction::new, context);
+    }
+
+    private ExitDesktopTaskTransitionHandler(
+            Transitions transitions,
+            Supplier<SurfaceControl.Transaction> supplier,
+            Context context) {
+        mTransitions = transitions;
+        mTransactionSupplier = supplier;
+        mContext = context;
+    }
+
+    /**
+     * Starts Transition of a given type
+     * @param type Transition type
+     * @param wct WindowContainerTransaction for transition
+     */
+    public void startTransition(@WindowManager.TransitionType int type,
+            @NonNull WindowContainerTransaction wct) {
+        final IBinder token = mTransitions.startTransition(type, wct, this);
+        mPendingTransitionTokens.add(token);
+    }
+
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        boolean transitionHandled = false;
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
+                continue;
+            }
+
+            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+            if (taskInfo == null || taskInfo.taskId == -1) {
+                continue;
+            }
+
+            if (change.getMode() == WindowManager.TRANSIT_CHANGE) {
+                transitionHandled |= startChangeTransition(
+                        transition, info.getType(), change, startT, finishCallback);
+            }
+        }
+
+        mPendingTransitionTokens.remove(transition);
+
+        return transitionHandled;
+    }
+
+    @VisibleForTesting
+    boolean startChangeTransition(
+            @NonNull IBinder transition,
+            @WindowManager.TransitionType int type,
+            @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        if (!mPendingTransitionTokens.contains(transition)) {
+            return false;
+        }
+        final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+        if (type == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+                && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+            // This Transition animates a task to fullscreen after being dragged to status bar
+            final Resources resources = mContext.getResources();
+            final DisplayMetrics metrics = resources.getDisplayMetrics();
+            final int screenWidth = metrics.widthPixels;
+            final int screenHeight = metrics.heightPixels;
+            final SurfaceControl sc = change.getLeash();
+            startT.setCrop(sc, null);
+            startT.apply();
+            final ValueAnimator animator = new ValueAnimator();
+            animator.setFloatValues(0f, 1f);
+            animator.setDuration(FULLSCREEN_ANIMATION_DURATION);
+            final Rect startBounds = change.getStartAbsBounds();
+            final float scaleX = (float) startBounds.width() / screenWidth;
+            final float scaleY = (float) startBounds.height() / screenHeight;
+            final SurfaceControl.Transaction t = mTransactionSupplier.get();
+            Point startPos = new Point(startBounds.left,
+                    startBounds.top);
+            animator.addUpdateListener(animation -> {
+                float fraction = animation.getAnimatedFraction();
+                float currentScaleX = scaleX + ((1 - scaleX) * fraction);
+                float currentScaleY = scaleY + ((1 - scaleY) * fraction);
+                t.setPosition(sc, startPos.x * (1 - fraction), startPos.y * (1 - fraction));
+                t.setScale(sc, currentScaleX, currentScaleY);
+                t.apply();
+            });
+            animator.start();
+            return true;
+        }
+
+        return false;
+    }
+
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        return null;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
index afddfab..ec09827 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
@@ -1,2 +1,3 @@
 # WM shell sub-module pip owner
 hwwang@google.com
+mateuszc@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f2f30ea..984c3c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -73,6 +73,7 @@
 import android.window.TaskSnapshot;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
@@ -142,6 +143,23 @@
     protected final ShellTaskOrganizer mTaskOrganizer;
     protected final ShellExecutor mMainExecutor;
 
+    // the runnable to execute after WindowContainerTransactions is applied to finish resizing pip
+    private Runnable mPipFinishResizeWCTRunnable;
+
+    private final WindowContainerTransactionCallback mPipFinishResizeWCTCallback =
+            new WindowContainerTransactionCallback() {
+        @Override
+        public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+            t.apply();
+
+            // execute the runnable if non-null after WCT is applied to finish resizing pip
+            if (mPipFinishResizeWCTRunnable != null) {
+                mPipFinishResizeWCTRunnable.run();
+                mPipFinishResizeWCTRunnable = null;
+            }
+        }
+    };
+
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
             new PipAnimationController.PipAnimationCallback() {
@@ -1249,8 +1267,23 @@
     /**
      * Animates resizing of the pinned stack given the duration and start bounds.
      * This is used when the starting bounds is not the current PiP bounds.
+     *
+     * @param pipFinishResizeWCTRunnable callback to run after window updates are complete
      */
     public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
+            float startingAngle, Consumer<Rect> updateBoundsCallback,
+            Runnable pipFinishResizeWCTRunnable) {
+        mPipFinishResizeWCTRunnable = pipFinishResizeWCTRunnable;
+        if (mPipFinishResizeWCTRunnable != null) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "mPipFinishResizeWCTRunnable is set to be called once window updates");
+        }
+
+        scheduleAnimateResizePip(fromBounds, toBounds, duration, startingAngle,
+                updateBoundsCallback);
+    }
+
+    private void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
             float startingAngle, Consumer<Rect> updateBoundsCallback) {
         if (mWaitForFixedRotation) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -1555,7 +1588,7 @@
             mSplitScreenOptional.ifPresent(splitScreenController ->
                     splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
         } else {
-            mTaskOrganizer.applyTransaction(wct);
+            mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index fee9140..956af70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -580,8 +580,16 @@
                 final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
                         mLastResizeBounds, movementBounds);
                 mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
+
+                // disable the pinch resizing until the final bounds are updated
+                final boolean prevEnablePinchResize = mEnablePinchResize;
+                mEnablePinchResize = false;
+
                 mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
-                        PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback);
+                        PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
+                            // reset the pinch resizing to its default state
+                            mEnablePinchResize = prevEnablePinchResize;
+                        });
             } else {
                 mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
                         PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 5c64177..c8d6a5e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -456,7 +456,10 @@
                         cancel(mWillFinishToHome);
                         return;
                     }
-                    hasChangingApp = true;
+                    // Don't consider order-only changes as changing apps.
+                    if (!TransitionUtil.isOrderOnly(change)) {
+                        hasChangingApp = true;
+                    }
                 }
             }
             if (hasChangingApp && foundRecentsClosing) {
@@ -484,13 +487,14 @@
             }
             boolean didMergeThings = false;
             if (closingTasks != null) {
-                // Cancelling a task-switch. Move the tasks back to mPausing from mOpening
+                // Potentially cancelling a task-switch. Move the tasks back to mPausing if they
+                // are in 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) {
-                        Slog.e(TAG, "Back to existing recents animation from an unrecognized "
-                                + "task: " + change.getTaskInfo().taskId);
+                        Slog.w(TAG, "Closing a task that wasn't opening, this may be split or"
+                                + " something unexpected: " + change.getTaskInfo().taskId);
                         continue;
                     }
                     mPausingTasks.add(mOpeningTasks.remove(openingIdx));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 32abd73..22800ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -66,13 +66,11 @@
     private final Transitions mTransitions;
     private final Runnable mOnFinish;
 
-    DismissTransition mPendingDismiss = null;
+    DismissSession mPendingDismiss = null;
     TransitSession mPendingEnter = null;
-    TransitSession mPendingRecent = null;
     TransitSession mPendingResize = null;
 
     private IBinder mAnimatingTransition = null;
-    OneShotRemoteHandler mPendingRemoteHandler = null;
     private OneShotRemoteHandler mActiveRemoteHandler = null;
 
     private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
@@ -101,27 +99,30 @@
         mFinishCallback = finishCallback;
         mAnimatingTransition = transition;
         mFinishTransaction = finishTransaction;
-        if (mPendingRemoteHandler != null) {
-            mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
-                    finishTransaction, mRemoteFinishCB);
-            mActiveRemoteHandler = mPendingRemoteHandler;
-            mPendingRemoteHandler = null;
-            return;
+
+        final TransitSession pendingTransition = getPendingTransition(transition);
+        if (pendingTransition != null) {
+            if (pendingTransition.mCanceled) {
+                // The pending transition was canceled, so skip playing animation.
+                startTransaction.apply();
+                onFinish(null /* wct */, null /* wctCB */);
+                return;
+            }
+
+            if (pendingTransition.mRemoteHandler != null) {
+                pendingTransition.mRemoteHandler.startAnimation(transition, info, startTransaction,
+                        finishTransaction, mRemoteFinishCB);
+                mActiveRemoteHandler = pendingTransition.mRemoteHandler;
+                return;
+            }
         }
+
         playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
     }
 
     private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
             @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
-        final TransitSession pendingTransition = getPendingTransition(transition);
-        if (pendingTransition != null && pendingTransition.mCanceled) {
-            // The pending transition was canceled, so skip playing animation.
-            t.apply();
-            onFinish(null /* wct */, null /* wctCB */);
-            return;
-        }
-
         // Play some place-holder fade animations
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
@@ -260,10 +261,6 @@
         return mPendingEnter != null && mPendingEnter.mTransition == transition;
     }
 
-    boolean isPendingRecent(IBinder transition) {
-        return mPendingRecent != null && mPendingRecent.mTransition == transition;
-    }
-
     boolean isPendingDismiss(IBinder transition) {
         return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
     }
@@ -276,8 +273,6 @@
     private TransitSession getPendingTransition(IBinder transition) {
         if (isPendingEnter(transition)) {
             return mPendingEnter;
-        } else if (isPendingRecent(transition)) {
-            return mPendingRecent;
         } else if (isPendingDismiss(transition)) {
             return mPendingDismiss;
         } else if (isPendingResize(transition)) {
@@ -311,14 +306,8 @@
             @Nullable RemoteTransition remoteTransition,
             @Nullable TransitionConsumedCallback consumedCallback,
             @Nullable TransitionFinishedCallback finishedCallback) {
-        mPendingEnter = new TransitSession(transition, consumedCallback, finishedCallback);
-
-        if (remoteTransition != null) {
-            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
-            mPendingRemoteHandler = new OneShotRemoteHandler(
-                    mTransitions.getMainExecutor(), remoteTransition);
-            mPendingRemoteHandler.setTransition(transition);
-        }
+        mPendingEnter = new TransitSession(
+                transition, consumedCallback, finishedCallback, remoteTransition);
 
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                 + " deduced Enter split screen");
@@ -344,7 +333,7 @@
     /** Sets a transition to dismiss split. */
     void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop,
             @SplitScreenController.ExitReason int reason) {
-        mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
+        mPendingDismiss = new DismissSession(transition, reason, dismissTop);
 
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                         + " deduced Dismiss due to %s. toTop=%s",
@@ -372,32 +361,10 @@
                 + " deduced Resize split screen");
     }
 
-    void setRecentTransition(@NonNull IBinder transition,
-            @Nullable RemoteTransition remoteTransition,
-            @Nullable TransitionFinishedCallback finishCallback) {
-        mPendingRecent = new TransitSession(transition, null /* consumedCb */, finishCallback);
-
-        if (remoteTransition != null) {
-            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
-            mPendingRemoteHandler = new OneShotRemoteHandler(
-                    mTransitions.getMainExecutor(), remoteTransition);
-            mPendingRemoteHandler.setTransition(transition);
-        }
-
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
-                + " deduced Enter recent panel");
-    }
-
     void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
             IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
         if (mergeTarget != mAnimatingTransition) return;
 
-        if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
-            // Since there's an entering transition merged, recent transition no longer
-            // need to handle entering split screen after the transition finished.
-            mPendingRecent.setFinishedCallback(null);
-        }
-
         if (mActiveRemoteHandler != null) {
             mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
         } else {
@@ -425,19 +392,13 @@
                 // An entering transition got merged, appends the rest operations to finish entering
                 // split screen.
                 mStageCoordinator.finishEnterSplitScreen(finishT);
-                mPendingRemoteHandler = null;
             }
 
             mPendingEnter.onConsumed(aborted);
             mPendingEnter = null;
-            mPendingRemoteHandler = null;
         } else if (isPendingDismiss(transition)) {
             mPendingDismiss.onConsumed(aborted);
             mPendingDismiss = null;
-        } else if (isPendingRecent(transition)) {
-            mPendingRecent.onConsumed(aborted);
-            mPendingRecent = null;
-            mPendingRemoteHandler = null;
         } else if (isPendingResize(transition)) {
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
@@ -451,9 +412,6 @@
         if (isPendingEnter(mAnimatingTransition)) {
             mPendingEnter.onFinished(wct, mFinishTransaction);
             mPendingEnter = null;
-        } else if (isPendingRecent(mAnimatingTransition)) {
-            mPendingRecent.onFinished(wct, mFinishTransaction);
-            mPendingRecent = null;
         } else if (isPendingDismiss(mAnimatingTransition)) {
             mPendingDismiss.onFinished(wct, mFinishTransaction);
             mPendingDismiss = null;
@@ -462,7 +420,6 @@
             mPendingResize = null;
         }
 
-        mPendingRemoteHandler = null;
         mActiveRemoteHandler = null;
         mAnimatingTransition = null;
 
@@ -568,10 +525,11 @@
     }
 
     /** Session for a transition and its clean-up callback. */
-    static class TransitSession {
+    class TransitSession {
         final IBinder mTransition;
         TransitionConsumedCallback mConsumedCallback;
         TransitionFinishedCallback mFinishedCallback;
+        OneShotRemoteHandler mRemoteHandler;
 
         /** Whether the transition was canceled. */
         boolean mCanceled;
@@ -579,10 +537,24 @@
         TransitSession(IBinder transition,
                 @Nullable TransitionConsumedCallback consumedCallback,
                 @Nullable TransitionFinishedCallback finishedCallback) {
+            this(transition, consumedCallback, finishedCallback, null /* remoteTransition */);
+        }
+
+        TransitSession(IBinder transition,
+                @Nullable TransitionConsumedCallback consumedCallback,
+                @Nullable TransitionFinishedCallback finishedCallback,
+                @Nullable RemoteTransition remoteTransition) {
             mTransition = transition;
             mConsumedCallback = consumedCallback;
             mFinishedCallback = finishedCallback;
 
+            if (remoteTransition != null) {
+                // Wrapping the remote transition for ease-of-use. (OneShot handles all the binder
+                // linking/death stuff)
+                mRemoteHandler = new OneShotRemoteHandler(
+                        mTransitions.getMainExecutor(), remoteTransition);
+                mRemoteHandler.setTransition(transition);
+            }
         }
 
         /** Sets transition consumed callback. */
@@ -621,11 +593,11 @@
     }
 
     /** Bundled information of dismiss transition. */
-    static class DismissTransition extends TransitSession {
+    class DismissSession extends TransitSession {
         final int mReason;
         final @SplitScreen.StageType int mDismissTop;
 
-        DismissTransition(IBinder transition, int reason, int dismissTop) {
+        DismissSession(IBinder transition, int reason, int dismissTop) {
             super(transition, null /* consumedCallback */, null /* finishedCallback */);
             this.mReason = reason;
             this.mDismissTop = dismissTop;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index c616587..dd91a37 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2226,15 +2226,9 @@
             } else if (isOpening && inFullscreen) {
                 final int activityType = triggerTask.getActivityType();
                 if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
-                    if (request.getRemoteTransition() != null) {
-                        // starting recents/home, so don't handle this and let it fall-through to
-                        // the remote handler.
-                        return null;
-                    }
-                    // Need to use the old stuff for non-remote animations, otherwise we don't
-                    // exit split-screen.
-                    mSplitTransitions.setRecentTransition(transition, null /* remote */,
-                            this::onRecentsInSplitAnimationFinish);
+                    // starting recents/home, so don't handle this and let it fall-through to
+                    // the remote handler.
+                    return null;
                 }
             }
         } else {
@@ -2363,8 +2357,6 @@
         if (mSplitTransitions.isPendingEnter(transition)) {
             shouldAnimate = startPendingEnterAnimation(
                     transition, info, startTransaction, finishTransaction);
-        } else if (mSplitTransitions.isPendingRecent(transition)) {
-            onRecentsInSplitAnimationStart(startTransaction);
         } else if (mSplitTransitions.isPendingDismiss(transition)) {
             shouldAnimate = startPendingDismissAnimation(
                     mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
@@ -2589,7 +2581,7 @@
     }
 
     private boolean startPendingDismissAnimation(
-            @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
+            @NonNull SplitScreenTransitions.DismissSession dismissTransition,
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
             @NonNull SurfaceControl.Transaction finishT) {
         prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
@@ -2626,7 +2618,7 @@
 
     /** Call this when the recents animation during split-screen finishes. */
     public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT, TransitionInfo info) {
         // Check if the recent transition is finished by returning to the current
         // split, so we can restore the divider bar.
         for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
@@ -2643,8 +2635,14 @@
             }
         }
 
+        // TODO(b/275664132): Remove dismissing split screen here to fit in back-to-split support.
         // Dismiss the split screen if it's not returning to split.
         prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if (change.getTaskInfo() != null && TransitionUtil.isClosingType(change.getMode())) {
+                finishT.setCrop(change.getLeash(), null).hide(change.getLeash());
+            }
+        }
         setSplitsVisible(false);
         setDividerVisibility(false, finishT);
         logExit(EXIT_REASON_UNKNOWN);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 646d55e..36c9077 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -387,8 +387,15 @@
             return;
         }
         // Sync Transactions can't operate simultaneously with shell transition collection.
-        // The transition animation (upon showing) will sync the location itself.
-        if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
+        if (isUsingShellTransitions()) {
+            if (mTaskViewTransitions.hasPending()) {
+                // There is already a transition in-flight. The window bounds will be synced
+                // once it is complete.
+                return;
+            }
+            mTaskViewTransitions.setTaskBounds(this, boundsOnScreen);
+            return;
+        }
 
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setBounds(mTaskToken, boundsOnScreen);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 9b995c5..3b1ce49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.taskview;
 
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -23,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.util.Slog;
 import android.view.SurfaceControl;
@@ -40,7 +42,7 @@
  * Handles Shell Transitions that involve TaskView tasks.
  */
 public class TaskViewTransitions implements Transitions.TransitionHandler {
-    private static final String TAG = "TaskViewTransitions";
+    static final String TAG = "TaskViewTransitions";
 
     private final ArrayList<TaskViewTaskController> mTaskViews = new ArrayList<>();
     private final ArrayList<PendingTransition> mPending = new ArrayList<>();
@@ -197,6 +199,13 @@
         // visibility is reported in transition.
     }
 
+    void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setBounds(taskView.getTaskInfo().token, boundsOnScreen);
+        mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
+        startNextTransition();
+    }
+
     private void startNextTransition() {
         if (mPending.isEmpty()) return;
         final PendingTransition pending = mPending.get(0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 586cab0..5a92f78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -187,17 +187,18 @@
                 && isOpeningType(request.getType())
                 && request.getTriggerTask() != null
                 && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                && (request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME
-                        || request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_RECENTS)
-                && request.getRemoteTransition() != null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+                && request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a going-home request while "
                     + "Split-Screen is active, so treat it as Mixed.");
             Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler =
                     mPlayer.dispatchRequest(transition, request, this);
             if (handler == null) {
-                android.util.Log.e(Transitions.TAG, "   No handler for remote? This is unexpected"
-                        + ", there should at-least be RemoteHandler.");
-                return null;
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                        " Lean on the remote transition handler to fetch a proper remote via"
+                                + " TransitionFilter");
+                handler = new Pair<>(
+                        mPlayer.getRemoteTransitionHandler(),
+                        new WindowContainerTransaction());
             }
             final MixedTransition mixed = new MixedTransition(
                     MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
@@ -234,6 +235,7 @@
     private TransitionInfo subCopy(@NonNull TransitionInfo info,
             @WindowManager.TransitionType int newType, boolean withChanges) {
         final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
+        out.setDebugId(info.getDebugId());
         if (withChanges) {
             for (int i = 0; i < info.getChanges().size(); ++i) {
                 out.getChanges().add(info.getChanges().get(i));
@@ -515,7 +517,7 @@
             // If pair-to-pair switching, the post-recents clean-up isn't needed.
             if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
                 wct = wct != null ? wct : new WindowContainerTransaction();
-                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
+                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction, info);
             }
             mSplitHandler.onTransitionAnimationComplete();
             finishCallback.onTransitionFinished(wct, wctCB);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 63c7969..3dd10a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -301,8 +301,8 @@
             return true;
         }
 
-        // check if no-animation and skip animation if so.
-        if (Transitions.isAllNoAnimation(info)) {
+        // Early check if the transition doesn't warrant an animation.
+        if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)) {
             startTransaction.apply();
             finishTransaction.apply();
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 485b400..4e3d220 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -63,7 +63,7 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         if (mTransition != transition) return false;
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
-                + " transition %s for %s.", mRemote, transition);
+                + " transition %s for #%d.", mRemote, info.getDebugId());
 
         final IBinder.DeathRecipient remoteDied = () -> {
             Log.e(Transitions.TAG, "Remote transition died, finishing");
@@ -113,9 +113,6 @@
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
-                + " transition %s for %s.", mRemote, transition);
-
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
             public void onTransitionFinished(WindowContainerTransaction wct,
@@ -154,4 +151,10 @@
                 + " for %s: %s", transition, remote);
         return new WindowContainerTransaction();
     }
+
+    @Override
+    public String toString() {
+        return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":"
+                + mRemote.getRemoteTransition();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 3c4e889..5b7231c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -101,8 +101,8 @@
         }
         RemoteTransition pendingRemote = mRequestedRemotes.get(transition);
         if (pendingRemote == null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
-                    + "explicit remote, search filters for match for %s", transition, info);
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition doesn't have "
+                    + "explicit remote, search filters for match for %s", info);
             // If no explicit remote, search filters until one matches
             for (int i = mFilters.size() - 1; i >= 0; --i) {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s",
@@ -116,8 +116,8 @@
                 }
             }
         }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for %s to %s",
-                transition, pendingRemote);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for #%d to %s",
+                info.getDebugId(), pendingRemote);
 
         if (pendingRemote == null) return false;
 
@@ -184,9 +184,10 @@
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget).getRemoteTransition();
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
-                transition, remote);
+        final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
+        final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "   Merge into remote: %s",
+                remoteTransition);
         if (remote == null) return;
 
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
index 0386ec3..1879bf7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IBinder;
-import android.util.Log;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -59,7 +58,6 @@
     @Override
     public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
             @Nullable SurfaceControl.Transaction finishTransaction) {
-        Log.e(Transitions.TAG, "Sleep transition was consumed. This doesn't make sense");
         mSleepTransitions.remove(transition);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index beabf18..681fa51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -140,6 +140,9 @@
     /** Transition type to freeform in desktop mode. */
     public static final int TRANSIT_ENTER_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 11;
 
+    /** Transition type to fullscreen from desktop mode. */
+    public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
+
     private final WindowOrganizer mOrganizer;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
@@ -182,6 +185,14 @@
 
         /** Ordered list of transitions which have been merged into this one. */
         private ArrayList<ActiveTransition> mMerged;
+
+        @Override
+        public String toString() {
+            if (mInfo != null && mInfo.getDebugId() >= 0) {
+                return "(#" + mInfo.getDebugId() + ")" + mToken;
+            }
+            return mToken.toString();
+        }
     }
 
     /** Keeps track of transitions which have been started, but aren't ready yet. */
@@ -324,6 +335,10 @@
         mRemoteTransitionHandler.removeFiltered(remoteTransition);
     }
 
+    RemoteTransitionHandler getRemoteTransitionHandler() {
+        return mRemoteTransitionHandler;
+    }
+
     /** Registers an observer on the lifecycle of transitions. */
     public void registerObserver(@NonNull TransitionObserver observer) {
         mObservers.add(observer);
@@ -512,6 +527,16 @@
         return hasNoAnimation;
     }
 
+    /**
+     * Check if all changes in this transition are only ordering changes. If so, we won't animate.
+     */
+    static boolean isAllOrderOnly(TransitionInfo info) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            if (!TransitionUtil.isOrderOnly(info.getChanges().get(i))) return false;
+        }
+        return true;
+    }
+
     @VisibleForTesting
     void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
@@ -525,8 +550,8 @@
                             activeTransition -> activeTransition.mToken).toArray()));
         }
         if (activeIdx > 0) {
-            Log.e(TAG, "Transition became ready out-of-order " + transitionToken + ". Expected"
-                    + " order: " + Arrays.toString(mPendingTransitions.stream().map(
+            Log.e(TAG, "Transition became ready out-of-order " + mPendingTransitions.get(activeIdx)
+                    + ". Expected order: " + Arrays.toString(mPendingTransitions.stream().map(
                             activeTransition -> activeTransition.mToken).toArray()));
         }
         // Move from pending to ready
@@ -543,6 +568,7 @@
         if (info.getType() == TRANSIT_SLEEP) {
             if (activeIdx > 0 || !mActiveTransitions.isEmpty() || mReadyTransitions.size() > 1) {
                 // Sleep starts a process of forcing all prior transitions to finish immediately
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Start finish-for-sleep");
                 finishForSleep(null /* forceFinish */);
                 return;
             }
@@ -551,8 +577,8 @@
         if (info.getRootCount() == 0 && !alwaysReportToKeyguard(info)) {
             // No root-leashes implies that the transition is empty/no-op, so just do
             // housekeeping and return.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots (%s): %s",
-                    transitionToken, info);
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+                    + " abort", active);
             onAbort(active);
             return;
         }
@@ -581,6 +607,8 @@
                 && allOccluded)) {
             // Treat this as an abort since we are bypassing any merge logic and effectively
             // finishing immediately.
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                    "Non-visible anim so abort: %s", active);
             onAbort(active);
             return;
         }
@@ -648,21 +676,21 @@
             return;
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
-                + " another transition %s is still animating. Notify the animating transition"
-                + " in case they can be merged", ready.mToken, playing.mToken);
+                + " %s is still animating. Notify the animating transition"
+                + " in case they can be merged", ready, playing);
         playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
                 playing.mToken, (wct, cb) -> onMerged(playing, ready));
     }
 
     private void onMerged(@NonNull ActiveTransition playing, @NonNull ActiveTransition merged) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged %s",
-                merged.mToken);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+                merged, playing);
         int readyIdx = 0;
         if (mReadyTransitions.isEmpty() || mReadyTransitions.get(0) != merged) {
-            Log.e(TAG, "Merged transition out-of-order?");
+            Log.e(TAG, "Merged transition out-of-order? " + merged);
             readyIdx = mReadyTransitions.indexOf(merged);
             if (readyIdx < 0) {
-                Log.e(TAG, "Merged a transition that is no-longer queued?");
+                Log.e(TAG, "Merged a transition that is no-longer queued? " + merged);
                 return;
             }
         }
@@ -683,6 +711,7 @@
     }
 
     private void playTransition(@NonNull ActiveTransition active) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
         for (int i = 0; i < mObservers.size(); ++i) {
             mObservers.get(i).onTransitionStarting(active.mToken);
         }
@@ -784,12 +813,12 @@
         int activeIdx = mActiveTransitions.indexOf(active);
         if (activeIdx < 0) {
             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
-                    + " a handler didn't properly deal with a merge. " + active.mToken,
+                    + " a handler didn't properly deal with a merge. " + active,
                     new RuntimeException());
             return;
         } else if (activeIdx != 0) {
             // Relevant right now since we only allow 1 active transition at a time.
-            Log.e(TAG, "Finishing a transition out of order. " + active.mToken);
+            Log.e(TAG, "Finishing a transition out of order. " + active);
         }
         mActiveTransitions.remove(activeIdx);
 
@@ -797,7 +826,7 @@
             mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
-                + "(aborted=%b), notifying core %s", active.mAborted, active.mToken);
+                + "(aborted=%b), notifying core %s", active.mAborted, active);
         if (active.mStartT != null) {
             // Applied by now, so clear immediately to remove any references. Do not set to null
             // yet, though, since nullness is used later to disambiguate malformed transitions.
@@ -913,6 +942,8 @@
     /** Start a new transition directly. */
     public IBinder startTransition(@WindowManager.TransitionType int type,
             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+                + "type=%d wct=%s handler=%s", type, wct, handler);
         final ActiveTransition active = new ActiveTransition();
         active.mHandler = handler;
         active.mToken = mOrganizer.startNewTransition(type, wct);
@@ -944,8 +975,7 @@
             return;
         }
         if (forceFinish != null && mActiveTransitions.contains(forceFinish)) {
-            Log.e(TAG, "Forcing transition to finish due to sleep timeout: "
-                    + forceFinish.mToken);
+            Log.e(TAG, "Forcing transition to finish due to sleep timeout: " + forceFinish);
             forceFinish.mAborted = true;
             // Last notify of it being consumed. Note: mHandler should never be null,
             // but check just to be safe.
@@ -963,6 +993,8 @@
                 // Try to signal that we are sleeping by attempting to merge the sleep transition
                 // into the playing one.
                 final ActiveTransition nextSleep = mReadyTransitions.get(sleepIdx);
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge SLEEP %s"
+                        + " into %s", nextSleep, playing);
                 playing.mHandler.mergeAnimation(nextSleep.mToken, nextSleep.mInfo, dummyT,
                         playing.mToken, (wct, cb) -> {});
             } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index 7595c96..ce10291 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -31,6 +31,7 @@
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -90,6 +91,15 @@
                 && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
     }
 
+    /** Returns `true` if `change` is only re-ordering. */
+    public static boolean isOrderOnly(TransitionInfo.Change change) {
+        return change.getMode() == TRANSIT_CHANGE
+                && (change.getFlags() & FLAG_MOVED_TO_TOP) != 0
+                && change.getStartAbsBounds().equals(change.getEndAbsBounds())
+                && (change.getLastParent() == null
+                        || change.getLastParent().equals(change.getParent()));
+    }
+
     /**
      * Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
      * MUST call `test` in the same order that the changes appear in the TransitionInfo.
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 f943e52..c0dcd0b 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
@@ -26,16 +26,20 @@
 import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
 import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.util.DisplayMetrics;
 import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.InputChannel;
@@ -541,9 +545,7 @@
                     if (ev.getY() > statusBarHeight) {
                         if (DesktopModeStatus.isProto2Enabled()) {
                             mPauseRelayoutForTask = relevantDecor.mTaskInfo.taskId;
-                            mDesktopTasksController.ifPresent(
-                                    c -> c.moveToDesktopWithAnimation(relevantDecor.mTaskInfo,
-                                            getFreeformBounds(ev)));
+                            centerAndMoveToDesktopWithAnimation(relevantDecor, ev);
                         } else if (DesktopModeStatus.isProto1Enabled()) {
                             mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
                         }
@@ -596,25 +598,59 @@
         }
     }
 
-    private Rect getFreeformBounds(@NonNull MotionEvent ev) {
-        final Rect endBounds = new Rect();
-        final int finalWidth = (int) (FINAL_FREEFORM_SCALE
-                * mDragToDesktopAnimationStartBounds.width());
-        final int finalHeight = (int) (FINAL_FREEFORM_SCALE
-                * mDragToDesktopAnimationStartBounds.height());
+    /**
+     * Gets bounds of a scaled window centered relative to the screen bounds
+     * @param scale the amount to scale to relative to the Screen Bounds
+     */
+    private Rect calculateFreeformBounds(float scale) {
+        final Resources resources = mContext.getResources();
+        final DisplayMetrics metrics = resources.getDisplayMetrics();
+        final int screenWidth = metrics.widthPixels;
+        final int screenHeight = metrics.heightPixels;
 
-        endBounds.left = mDragToDesktopAnimationStartBounds.centerX() - finalWidth / 2
-                + (int) (ev.getX() - mCaptionDragStartX);
-        endBounds.right = endBounds.left + (int) (FINAL_FREEFORM_SCALE
-                * mDragToDesktopAnimationStartBounds.width());
-        endBounds.top = (int) (ev.getY()
-                - ((FINAL_FREEFORM_SCALE - DRAG_FREEFORM_SCALE)
-                * mDragToDesktopAnimationStartBounds.height() / 2));
-        endBounds.bottom = endBounds.top + finalHeight;
-
+        final float adjustmentPercentage = (1f - scale) / 2;
+        final Rect endBounds = new Rect((int) (screenWidth * adjustmentPercentage),
+                (int) (screenHeight * adjustmentPercentage),
+                (int) (screenWidth * (adjustmentPercentage + scale)),
+                (int) (screenHeight * (adjustmentPercentage + scale)));
         return endBounds;
     }
 
+    /**
+     * Animates a window to the center, grows to freeform size, and transitions to Desktop Mode.
+     * @param relevantDecor the window decor of the task to be animated
+     * @param ev the motion event that triggers the animation
+     */
+    private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor,
+            MotionEvent ev) {
+        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.setDuration(FREEFORM_ANIMATION_DURATION);
+        final SurfaceControl sc = relevantDecor.mTaskSurface;
+        final Rect endBounds = calculateFreeformBounds(DRAG_FREEFORM_SCALE);
+        final Transaction t = mTransactionFactory.get();
+        final float diffX = endBounds.centerX() - ev.getX();
+        final float diffY = endBounds.top - ev.getY();
+        final float startingX = ev.getX() - DRAG_FREEFORM_SCALE
+                * mDragToDesktopAnimationStartBounds.width() / 2;
+
+        animator.addUpdateListener(animation -> {
+            final float animatorValue = (float) animation.getAnimatedValue();
+            final float x = startingX + diffX * animatorValue;
+            final float y = ev.getY() + diffY * animatorValue;
+            t.setPosition(sc, x, y);
+            t.apply();
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDesktopTasksController.ifPresent(
+                        c -> c.moveToDesktopWithAnimation(relevantDecor.mTaskInfo,
+                                calculateFreeformBounds(FINAL_FREEFORM_SCALE)));
+            }
+        });
+        animator.start();
+    }
+
     private void startAnimation(@NonNull DesktopModeWindowDecoration focusedDecor) {
         mDragToDesktopValueAnimator = ValueAnimator.ofFloat(1f, DRAG_FREEFORM_SCALE);
         mDragToDesktopValueAnimator.setDuration(FREEFORM_ANIMATION_DURATION);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index c45e3fc..e08d40d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -17,11 +17,15 @@
 package com.android.wm.shell.windowdecor;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -34,7 +38,8 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 import android.window.WindowContainerTransaction;
@@ -71,17 +76,17 @@
     private DragDetector mDragDetector;
 
     private RelayoutParams mRelayoutParams = new RelayoutParams();
-    private final int mCaptionMenuHeightId = R.dimen.freeform_decor_caption_menu_height;
-    private final int mCaptionMenuHeightWithoutWindowingControlsId =
-            R.dimen.freeform_decor_caption_menu_height_no_windowing_controls;
     private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
             new WindowDecoration.RelayoutResult<>();
 
-    private AdditionalWindow mHandleMenu;
-    private final int mHandleMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
-    private final int mHandleMenuShadowRadiusId = R.dimen.caption_menu_shadow_radius;
-    private final int mHandleMenuCornerRadiusId = R.dimen.caption_menu_corner_radius;
-    private PointF mHandleMenuPosition = new PointF();
+    private final PointF mHandleMenuAppInfoPillPosition = new PointF();
+    private final PointF mHandleMenuWindowingPillPosition = new PointF();
+    private final PointF mHandleMenuMoreActionsPillPosition = new PointF();
+
+    // Collection of additional windows that comprise the handle menu.
+    private AdditionalWindow mHandleMenuAppInfoPill;
+    private AdditionalWindow mHandleMenuWindowingPill;
+    private AdditionalWindow mHandleMenuMoreActionsPill;
 
     private Drawable mAppIcon;
     private CharSequence mAppName;
@@ -234,30 +239,8 @@
                 mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
     }
 
-    private void setupHandleMenu() {
-        final View menu = mHandleMenu.mWindowViewHost.getView();
-        final View fullscreen = menu.findViewById(R.id.fullscreen_button);
-        fullscreen.setOnClickListener(mOnCaptionButtonClickListener);
-        final View desktop = menu.findViewById(R.id.desktop_button);
-        desktop.setOnClickListener(mOnCaptionButtonClickListener);
-        final ViewGroup windowingBtns = menu.findViewById(R.id.windowing_mode_buttons);
-        windowingBtns.setVisibility(DesktopModeStatus.isProto1Enabled() ? View.GONE : View.VISIBLE);
-        final View split = menu.findViewById(R.id.split_screen_button);
-        split.setOnClickListener(mOnCaptionButtonClickListener);
-        final View close = menu.findViewById(R.id.close_button);
-        close.setOnClickListener(mOnCaptionButtonClickListener);
-        final View collapse = menu.findViewById(R.id.collapse_menu_button);
-        collapse.setOnClickListener(mOnCaptionButtonClickListener);
-        menu.setOnTouchListener(mOnCaptionTouchListener);
-
-        final ImageView appIcon = menu.findViewById(R.id.application_icon);
-        final TextView appName = menu.findViewById(R.id.application_name);
-        appIcon.setImageDrawable(mAppIcon);
-        appName.setText(mAppName);
-    }
-
     boolean isHandleMenuActive() {
-        return mHandleMenu != null;
+        return mHandleMenuAppInfoPill != null;
     }
 
     private void loadAppInfo() {
@@ -291,34 +274,142 @@
         final Resources resources = mDecorWindowContext.getResources();
         final int captionWidth = mTaskInfo.getConfiguration()
                 .windowConfiguration.getBounds().width();
-        final int menuWidth = loadDimensionPixelSize(resources, mHandleMenuWidthId);
-        // The windowing controls are disabled in proto1.
-        final int menuHeight = loadDimensionPixelSize(resources, DesktopModeStatus.isProto1Enabled()
-                ? mCaptionMenuHeightWithoutWindowingControlsId : mCaptionMenuHeightId);
-        final int shadowRadius = loadDimensionPixelSize(resources, mHandleMenuShadowRadiusId);
-        final int cornerRadius = loadDimensionPixelSize(resources, mHandleMenuCornerRadiusId);
+        final int menuWidth = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_width);
+        final int shadowRadius = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_shadow_radius);
+        final int cornerRadius = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_corner_radius);
+        final int marginMenuTop = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_margin_top);
+        final int marginMenuStart = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_margin_start);
+        final int marginMenuSpacing = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_pill_spacing_margin);
+        final int appInfoPillHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_app_info_pill_height);
+        final int windowingPillHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_windowing_pill_height);
+        final int moreActionsPillHeight = loadDimensionPixelSize(resources,
+                R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
 
-        final int x, y;
+        final int menuX, menuY;
         if (mRelayoutParams.mLayoutResId
                 == R.layout.desktop_mode_app_controls_window_decor) {
             // Align the handle menu to the left of the caption.
-            x = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX;
-            y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+            menuX = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX + marginMenuStart;
+            menuY = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY + marginMenuTop;
         } else {
             // Position the handle menu at the center of the caption.
-            x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
+            menuX = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
                     - mResult.mDecorContainerOffsetX;
-            y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+            menuY = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY + marginMenuStart;
         }
-        mHandleMenuPosition.set(x, y);
-        final String namePrefix = "Caption Menu";
-        mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
-                menuWidth, menuHeight, shadowRadius, cornerRadius);
+
+        final int appInfoPillY = menuY;
+        createAppInfoPill(t, menuX, appInfoPillY, menuWidth, appInfoPillHeight, shadowRadius,
+                cornerRadius);
+
+        // Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
+        final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
+        final int windowingPillY = appInfoPillY + appInfoPillHeight + marginMenuSpacing;
+        if (shouldShowWindowingPill) {
+            createWindowingPill(t, menuX, windowingPillY, menuWidth, windowingPillHeight,
+                    shadowRadius,
+                    cornerRadius);
+        }
+
+        final int moreActionsPillY;
+        if (shouldShowWindowingPill) {
+            // Take into account the windowing pill height and margins.
+            moreActionsPillY = windowingPillY + windowingPillHeight + marginMenuSpacing;
+        } else {
+            // Just start after the end of the app info pill + margins.
+            moreActionsPillY = appInfoPillY + appInfoPillHeight + marginMenuSpacing;
+        }
+        createMoreActionsPill(t, menuX, moreActionsPillY, menuWidth, moreActionsPillHeight,
+                shadowRadius, cornerRadius);
+
         mSyncQueue.runInSync(transaction -> {
             transaction.merge(t);
             t.close();
         });
-        setupHandleMenu();
+        setupHandleMenu(shouldShowWindowingPill);
+    }
+
+    private void createAppInfoPill(SurfaceControl.Transaction t, int x, int y, int width,
+            int height, int shadowRadius, int cornerRadius) {
+        mHandleMenuAppInfoPillPosition.set(x, y);
+        mHandleMenuAppInfoPill = addWindow(
+                R.layout.desktop_mode_window_decor_handle_menu_app_info_pill,
+                "Menu's app info pill",
+                t, x, y, width, height, shadowRadius, cornerRadius);
+    }
+
+    private void createWindowingPill(SurfaceControl.Transaction t, int x, int y, int width,
+            int height, int shadowRadius, int cornerRadius) {
+        mHandleMenuWindowingPillPosition.set(x, y);
+        mHandleMenuWindowingPill = addWindow(
+                R.layout.desktop_mode_window_decor_handle_menu_windowing_pill,
+                "Menu's windowing pill",
+                t, x, y, width, height, shadowRadius, cornerRadius);
+    }
+
+    private void createMoreActionsPill(SurfaceControl.Transaction t, int x, int y, int width,
+            int height, int shadowRadius, int cornerRadius) {
+        mHandleMenuMoreActionsPillPosition.set(x, y);
+        mHandleMenuMoreActionsPill = addWindow(
+                R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill,
+                "Menu's more actions pill",
+                t, x, y, width, height, shadowRadius, cornerRadius);
+    }
+
+    private void setupHandleMenu(boolean windowingPillShown) {
+        // App Info pill setup.
+        final View appInfoPillView = mHandleMenuAppInfoPill.mWindowViewHost.getView();
+        final ImageButton collapseBtn = appInfoPillView.findViewById(R.id.collapse_menu_button);
+        final ImageView appIcon = appInfoPillView.findViewById(R.id.application_icon);
+        final TextView appName = appInfoPillView.findViewById(R.id.application_name);
+        collapseBtn.setOnClickListener(mOnCaptionButtonClickListener);
+        appInfoPillView.setOnTouchListener(mOnCaptionTouchListener);
+        appIcon.setImageDrawable(mAppIcon);
+        appName.setText(mAppName);
+
+        // Windowing pill setup.
+        if (windowingPillShown) {
+            final View windowingPillView = mHandleMenuWindowingPill.mWindowViewHost.getView();
+            final ImageButton fullscreenBtn = windowingPillView.findViewById(
+                    R.id.fullscreen_button);
+            final ImageButton splitscreenBtn = windowingPillView.findViewById(
+                    R.id.split_screen_button);
+            final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button);
+            final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button);
+            fullscreenBtn.setOnClickListener(mOnCaptionButtonClickListener);
+            splitscreenBtn.setOnClickListener(mOnCaptionButtonClickListener);
+            floatingBtn.setOnClickListener(mOnCaptionButtonClickListener);
+            desktopBtn.setOnClickListener(mOnCaptionButtonClickListener);
+            // The button corresponding to the windowing mode that the task is currently in uses a
+            // different color than the others.
+            final ColorStateList activeColorStateList = ColorStateList.valueOf(
+                    mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active));
+            final ColorStateList inActiveColorStateList = ColorStateList.valueOf(
+                    mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive));
+            fullscreenBtn.setImageTintList(
+                    mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                            ? activeColorStateList : inActiveColorStateList);
+            splitscreenBtn.setImageTintList(
+                    mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+                            ? activeColorStateList : inActiveColorStateList);
+            floatingBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED
+                    ? activeColorStateList : inActiveColorStateList);
+            desktopBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+                    ? activeColorStateList : inActiveColorStateList);
+        }
+
+        // More Actions pill setup.
+        final View moreActionsPillView = mHandleMenuMoreActionsPill.mWindowViewHost.getView();
+        final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button);
+        closeBtn.setOnClickListener(mOnCaptionButtonClickListener);
     }
 
     /**
@@ -326,8 +417,14 @@
      */
     void closeHandleMenu() {
         if (!isHandleMenuActive()) return;
-        mHandleMenu.releaseView();
-        mHandleMenu = null;
+        mHandleMenuAppInfoPill.releaseView();
+        mHandleMenuAppInfoPill = null;
+        if (mHandleMenuWindowingPill != null) {
+            mHandleMenuWindowingPill.releaseView();
+            mHandleMenuWindowingPill = null;
+        }
+        mHandleMenuMoreActionsPill.releaseView();
+        mHandleMenuMoreActionsPill = null;
     }
 
     @Override
@@ -346,12 +443,29 @@
 
         // When this is called before the layout is fully inflated, width will be 0.
         // Menu is not visible in this scenario, so skip the check if that is the case.
-        if (mHandleMenu.mWindowViewHost.getView().getWidth() == 0) return;
+        if (mHandleMenuAppInfoPill.mWindowViewHost.getView().getWidth() == 0) return;
 
         PointF inputPoint = offsetCaptionLocation(ev);
-        if (!pointInView(mHandleMenu.mWindowViewHost.getView(),
-                inputPoint.x - mHandleMenuPosition.x - mResult.mDecorContainerOffsetX,
-                inputPoint.y - mHandleMenuPosition.y - mResult.mDecorContainerOffsetY)) {
+        final boolean pointInAppInfoPill = pointInView(
+                mHandleMenuAppInfoPill.mWindowViewHost.getView(),
+                inputPoint.x - mHandleMenuAppInfoPillPosition.x - mResult.mDecorContainerOffsetX,
+                inputPoint.y - mHandleMenuAppInfoPillPosition.y
+                        - mResult.mDecorContainerOffsetY);
+        boolean pointInWindowingPill = false;
+        if (mHandleMenuWindowingPill != null) {
+            pointInWindowingPill = pointInView(mHandleMenuWindowingPill.mWindowViewHost.getView(),
+                    inputPoint.x - mHandleMenuWindowingPillPosition.x
+                            - mResult.mDecorContainerOffsetX,
+                    inputPoint.y - mHandleMenuWindowingPillPosition.y
+                            - mResult.mDecorContainerOffsetY);
+        }
+        final boolean pointInMoreActionsPill = pointInView(
+                mHandleMenuMoreActionsPill.mWindowViewHost.getView(),
+                inputPoint.x - mHandleMenuMoreActionsPillPosition.x
+                        - mResult.mDecorContainerOffsetX,
+                inputPoint.y - mHandleMenuMoreActionsPillPosition.y
+                        - mResult.mDecorContainerOffsetY);
+        if (!pointInAppInfoPill && !pointInWindowingPill && !pointInMoreActionsPill) {
             closeHandleMenu();
         }
     }
@@ -408,14 +522,13 @@
             final View handle = caption.findViewById(R.id.caption_handle);
             clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
         } else {
-            final View menu = mHandleMenu.mWindowViewHost.getView();
-            final int captionWidth = mTaskInfo.getConfiguration().windowConfiguration
-                    .getBounds().width();
-            final int menuX = mRelayoutParams.mCaptionX + (captionWidth / 2)
-                    - (menu.getWidth() / 2);
-            final PointF inputPoint = new PointF(ev.getX() - menuX, ev.getY());
-            final View collapse = menu.findViewById(R.id.collapse_menu_button);
-            if (clickIfPointInView(inputPoint, collapse)) return;
+            final View appInfoPill = mHandleMenuAppInfoPill.mWindowViewHost.getView();
+            final ImageButton collapse = appInfoPill.findViewById(R.id.collapse_menu_button);
+            // Translate the input point from display coordinates to the same space as the collapse
+            // button, meaning its parent (app info pill view).
+            final PointF inputPoint = new PointF(ev.getX() - mHandleMenuAppInfoPillPosition.x,
+                    ev.getY() - mHandleMenuAppInfoPillPosition.y);
+            clickIfPointInView(inputPoint, collapse);
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 91846fa..e986ee1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -218,36 +218,36 @@
     assertLayers {
         if (landscapePosLeft) {
             splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
-            .then()
-            .isInvisible(component)
-            .then()
-            .splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
+                .then()
+                .isInvisible(component)
+                .then()
+                .splitAppLayerBoundsSnapToDivider(
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
         } else {
             splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
-            .then()
-            .isInvisible(component)
-            .then()
-            .splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
+                .then()
+                .isInvisible(component)
+                .then()
+                .splitAppLayerBoundsSnapToDivider(
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
new file mode 100644
index 0000000..d01a0ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.content.Context
+import android.system.helpers.CommandsHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import com.android.wm.shell.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.IFlickerTestData
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import org.junit.Assume
+import org.junit.Before
+import org.junit.runners.Parameterized
+
+abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
+    protected val context: Context = instrumentation.context
+    protected val letterboxApp = LetterboxAppHelper(instrumentation)
+    lateinit var cmdHelper: CommandsHelper
+    lateinit var letterboxStyle: HashMap<String, String>
+
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            setup {
+                setStartRotation()
+                letterboxApp.launchViaIntent(wmHelper)
+                setEndRotation()
+            }
+        }
+
+    @Before
+    fun before() {
+        cmdHelper = CommandsHelper.getInstance(instrumentation)
+        Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
+    }
+
+    private fun mapLetterboxStyle(): HashMap<String, String> {
+        val res = cmdHelper.executeShellCommand("wm get-letterbox-style")
+        val lines = res.lines()
+        val map = HashMap<String, String>()
+        for (line in lines) {
+            val keyValuePair = line.split(":")
+            if (keyValuePair.size == 2) {
+                val key = keyValuePair[0].trim()
+                map[key] = keyValuePair[1].trim()
+            }
+        }
+        return map
+    }
+
+    private fun isIgnoreOrientationRequest(): Boolean {
+        val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request")
+        return res != null && res.contains("true")
+    }
+
+    fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
+
+    fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)
+
+    /** Checks that app entering letterboxed state have rounded corners */
+    fun assertLetterboxAppAtStartHasRoundedCorners() {
+        assumeLetterboxRoundedCornersEnabled()
+        flicker.assertLayersStart { this.hasRoundedCorners(letterboxApp) }
+    }
+
+    fun assertLetterboxAppAtEndHasRoundedCorners() {
+        assumeLetterboxRoundedCornersEnabled()
+        flicker.assertLayersEnd { this.hasRoundedCorners(letterboxApp) }
+    }
+
+    /** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */
+    private fun assumeLetterboxRoundedCornersEnabled() {
+        if (!::letterboxStyle.isInitialized) {
+            letterboxStyle = mapLetterboxStyle()
+        }
+        Assume.assumeTrue(letterboxStyle.getValue("Corner radius") != "0")
+    }
+
+    fun assertLetterboxAppVisibleAtStartAndEnd() {
+        flicker.appWindowIsVisibleAtStart(letterboxApp)
+        flicker.appWindowIsVisibleAtEnd(letterboxApp)
+    }
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTest> {
+            return FlickerTestFactory.rotationTests()
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
new file mode 100644
index 0000000..c57100e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest`
+ *
+ * Actions:
+ * ```
+ *     Rotate non resizable portrait only app to opposite orientation to trigger size compat mode
+ * ```
+ * Notes:
+ * ```
+ *     Some default assertions (e.g., nav bar, status bar and screen covered)
+ *     are inherited [BaseTest]
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            setup {
+                setStartRotation()
+                letterboxApp.launchViaIntent(wmHelper)
+            }
+            transitions { setEndRotation() }
+            teardown { letterboxApp.exit(wmHelper) }
+        }
+
+    /**
+     * Windows maybe recreated when rotated. Checks that the focus does not change or if it does,
+     * focus returns to [letterboxApp]
+     */
+    @Postsubmit
+    @Test
+    fun letterboxAppFocusedAtEnd() = flicker.assertEventLog { focusChanges(letterboxApp.`package`) }
+
+    @Postsubmit
+    @Test
+    fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners()
+
+    /**
+     * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
+     * flicker, and disappears before the transition is complete
+     */
+    @Postsubmit
+    @Test
+    fun rotationLayerAppearsAndVanishes() {
+        flicker.assertLayers {
+            this.isVisible(letterboxApp)
+                .then()
+                .isVisible(ComponentNameMatcher.ROTATION)
+                .then()
+                .isVisible(letterboxApp)
+                .isInvisible(ComponentNameMatcher.ROTATION)
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
new file mode 100644
index 0000000..f111a8d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.helpers.WindowUtils
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test restarting app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest`
+ *
+ * Actions:
+ * ```
+ *     Rotate app to opposite orientation to trigger size compat mode
+ *     Press restart button and wait for letterboxed app to resize
+ * ```
+ * Notes:
+ * ```
+ *     Some default assertions (e.g., nav bar, status bar and screen covered)
+ *     are inherited [BaseTest]
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            super.transition(this)
+            transitions { letterboxApp.clickRestart(wmHelper) }
+            teardown { letterboxApp.exit(wmHelper) }
+        }
+
+    @Postsubmit
+    @Test
+    fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd()
+
+    @Postsubmit
+    @Test
+    fun appLayerVisibilityChanges() {
+        flicker.assertLayers {
+            this.isVisible(letterboxApp)
+                .then()
+                .isInvisible(letterboxApp)
+                .then()
+                .isVisible(letterboxApp)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtStartHasRoundedCorners()
+
+    /** Checks that the visible region of [letterboxApp] is still within display bounds */
+    @Postsubmit
+    @Test
+    fun appWindowRemainInsideVisibleBounds() {
+        val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
+        flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 1045a5a..93ee699 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -40,6 +40,7 @@
  *     Select "Auto-enter PiP" radio button
  *     Press Home button or swipe up to go Home and put [pipApp] in pip mode
  * ```
+ *
  * Notes:
  * ```
  *     1. All assertions are inherited from [EnterPipTest]
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 2d2588e..59918fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -38,6 +38,7 @@
  *     Launch an app in pip mode [pipApp],
  *     Swipe the pip window to the bottom-center of the screen and wait it disappear
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index e540ad5..d165832 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -38,6 +38,7 @@
  *     Click on the pip window
  *     Click on dismiss button and wait window disappear
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index e079d547..db18edb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -53,6 +53,7 @@
  *     Launch [pipApp] on a fixed landscape orientation
  *     Broadcast action [ACTION_ENTER_PIP] to enter pip mode
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index e40e5ea..51f0136 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -44,9 +44,7 @@
     @Presubmit
     @Test
     open fun pipAppLayerAlwaysVisible() {
-        flicker.assertLayers {
-            this.isVisible(pipApp)
-        }
+        flicker.assertLayers { this.isVisible(pipApp) }
     }
 
     /** Checks the content overlay appears then disappears during the animation */
@@ -55,11 +53,7 @@
     open fun pipOverlayLayerAppearThenDisappear() {
         val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
         flicker.assertLayers {
-            this.notContains(overlay)
-                .then()
-                .contains(overlay)
-                .then()
-                .notContains(overlay)
+            this.notContains(overlay).then().contains(overlay).then().notContains(overlay)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index 1f060e9..f1925d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -35,6 +35,7 @@
  *     Launch an app in full screen
  *     Press an "enter pip" button to put [pipApp] in pip mode
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 313631c..43f7e00b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -42,6 +42,7 @@
  *     Expand [pipApp] app to full screen by clicking on the pip window and
  *     then on the expand button
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index 93ffdd8d..617b3da 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -41,6 +41,7 @@
  *     Launch another full screen mode [testApp]
  *     Expand [pipApp] app to full screen via an intent
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 7d5f740..6deba1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -40,6 +40,7 @@
  *     Launch an app in pip mode [pipApp],
  *     Expand [pipApp] app to its maximum pip size by double clicking on it
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 9c00744..d8d57d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -40,6 +40,7 @@
  *     Launch [testApp]
  *     Check if pip window moves down (visually)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index c8d5624..ae3f879 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -40,6 +40,7 @@
  *     Press home
  *     Check if pip window moves up (visually)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 083cfd2..4e2a4e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -30,9 +30,7 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test the dragging of a PIP window.
- */
+/** Test the dragging of a PIP window. */
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -59,9 +57,7 @@
                 pipApp.exit(wmHelper)
                 tapl.setEnableRotation(false)
             }
-            transitions {
-                pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50)
-            }
+            transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) }
         }
 
     @Postsubmit
@@ -92,4 +88,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 53ce393..9fe9f52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -16,10 +16,10 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.graphics.Rect
 import android.platform.test.annotations.Postsubmit
 import android.tools.common.Rotation
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.graphics.Rect
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -33,14 +33,12 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test the snapping of a PIP window via dragging, releasing, and checking its final location.
- */
+/** Test the snapping of a PIP window via dragging, releasing, and checking its final location. */
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker){
+class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) {
     // represents the direction in which the pip window should be snapping
     private var willSnapRight: Boolean = true
 
@@ -60,8 +58,12 @@
 
                 // get the initial region bounds and cache them
                 val initRegion = pipApp.getWindowRect(wmHelper)
-                startBounds
-                        .set(initRegion.left, initRegion.top, initRegion.right, initRegion.bottom)
+                startBounds.set(
+                    initRegion.left,
+                    initRegion.top,
+                    initRegion.right,
+                    initRegion.bottom
+                )
 
                 // drag the pip window away from the edge
                 pipApp.dragPipWindowAwayFromEdge(wmHelper, 50)
@@ -108,4 +110,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 2cf8f61..703784d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -43,6 +43,7 @@
  *     Rotate the screen from [flicker.scenario.startRotation] to [flicker.scenario.endRotation]
  *     (usually, 0->90 and 90->0)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index bd2ffc1..2e81b30 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -71,7 +71,7 @@
     // TODO(b/245472831): Move back to presubmit after shell transitions landing.
     @FlakyTest(bugId = 245472831)
     @Test
-    fun secondaryAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(primaryApp)
+    fun secondaryAppLayerBecomesInvisible() = flicker.layerBecomesInvisible(secondaryApp)
 
     // TODO(b/245472831): Move back to presubmit after shell transitions landing.
     @FlakyTest(bugId = 245472831)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 17f174b..8938a2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -107,9 +107,7 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
+    @Presubmit @Test fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
 
     @Presubmit
     @Test
@@ -136,9 +134,7 @@
         )
 
     /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun entireScreenCovered() = super.entireScreenCovered()
+    @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 5b06c9c..fc006ab 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -79,8 +79,13 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
-            appExistAtStart = false)
+    fun cujCompleted() =
+        flicker.splitScreenEntered(
+            primaryApp,
+            secondaryApp,
+            fromOtherApp = false,
+            appExistAtStart = false
+        )
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index c840183..95de744 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -82,8 +82,8 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, sendNotificationApp,
-            fromOtherApp = false)
+    fun cujCompleted() =
+        flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 5c99209..533d5dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -80,8 +80,13 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
-            appExistAtStart = false)
+    fun cujCompleted() =
+        flicker.splitScreenEntered(
+            primaryApp,
+            secondaryApp,
+            fromOtherApp = false,
+            appExistAtStart = false
+        )
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 4ccc467..c9bd695 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -81,7 +81,8 @@
     @Mock lateinit var syncQueue: SyncTransactionQueue
     @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
     @Mock lateinit var transitions: Transitions
-    @Mock lateinit var transitionHandler: EnterDesktopTaskTransitionHandler
+    @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
+    @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
 
     lateinit var mockitoSession: StaticMockitoSession
     lateinit var controller: DesktopTasksController
@@ -117,7 +118,8 @@
             syncQueue,
             rootTaskDisplayAreaOrganizer,
             transitions,
-            transitionHandler,
+            enterDesktopTransitionHandler,
+            exitDesktopTransitionHandler,
             desktopModeTaskRepository,
             TestShellExecutor()
         )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
new file mode 100644
index 0000000..2c5a5cd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
+/** Tests of {@link com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler} */
+@SmallTest
+public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
+
+    @Mock
+    private Transitions mTransitions;
+    @Mock
+    IBinder mToken;
+    @Mock
+    Supplier<SurfaceControl.Transaction> mTransactionFactory;
+    @Mock
+    Context mContext;
+    @Mock
+    DisplayMetrics mDisplayMetrics;
+    @Mock
+    Resources mResources;
+    @Mock
+    SurfaceControl.Transaction mStartT;
+    @Mock
+    SurfaceControl.Transaction mFinishT;
+    @Mock
+    SurfaceControl.Transaction mAnimationT;
+    @Mock
+    Transitions.TransitionFinishCallback mTransitionFinishCallback;
+    @Mock
+    ShellExecutor mExecutor;
+
+    private ExitDesktopTaskTransitionHandler mExitDesktopTaskTransitionHandler;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(mExecutor).when(mTransitions).getMainExecutor();
+        doReturn(mAnimationT).when(mTransactionFactory).get();
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
+        when(mResources.getDisplayMetrics())
+                .thenReturn(getContext().getResources().getDisplayMetrics());
+
+        mExitDesktopTaskTransitionHandler = new ExitDesktopTaskTransitionHandler(mTransitions,
+                mContext);
+    }
+
+    @Test
+    public void testTransitExitDesktopModeAnimation() throws Throwable {
+        final int transitionType = Transitions.TRANSIT_EXIT_DESKTOP_MODE;
+        final int taskId = 1;
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        doReturn(mToken).when(mTransitions)
+                .startTransition(transitionType, wct, mExitDesktopTaskTransitionHandler);
+
+        mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct);
+
+        TransitionInfo.Change change =
+                createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN);
+        TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_EXIT_DESKTOP_MODE, change);
+        ArrayList<Exception> exceptions = new ArrayList<>();
+        runOnUiThread(() -> {
+            try {
+                assertTrue(mExitDesktopTaskTransitionHandler
+                        .startAnimation(mToken, info, mStartT, mFinishT,
+                                mTransitionFinishCallback));
+            } catch (Exception e) {
+                exceptions.add(e);
+            }
+        });
+        if (!exceptions.isEmpty()) {
+            throw exceptions.get(0);
+        }
+    }
+
+    private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
+            @WindowConfiguration.WindowingMode int windowingMode) {
+        final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+        taskInfo.taskId = taskId;
+        taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+        SurfaceControl.Builder b = new SurfaceControl.Builder()
+                .setName("test task");
+        final TransitionInfo.Change change = new TransitionInfo.Change(
+                taskInfo.token, b.build());
+        change.setMode(type);
+        change.setTaskInfo(taskInfo);
+        return change;
+    }
+
+    private static TransitionInfo createTransitionInfo(
+            @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) {
+        TransitionInfo info = new TransitionInfo(type, 0);
+        info.addChange(change);
+        return info;
+    }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 5b62a94..ada3455 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -159,7 +159,7 @@
         mPipResizeGestureHandler.onPinchResize(upEvent);
 
         verify(mPipTaskOrganizer, times(1))
-                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any(), any());
 
         assertTrue("The new size should be bigger than the original PiP size.",
                 mPipResizeGestureHandler.getLastResizeBounds().width()
@@ -198,7 +198,7 @@
         mPipResizeGestureHandler.onPinchResize(upEvent);
 
         verify(mPipTaskOrganizer, times(1))
-                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any(), any());
 
         assertTrue("The new size should be smaller than the original PiP size.",
                 mPipResizeGestureHandler.getLastResizeBounds().width()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index df78d92..a9f311f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -181,7 +181,7 @@
 
         IBinder transition = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(testRemote), mStageCoordinator, null, null);
+                new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -278,7 +278,7 @@
         // Make sure it cleans-up if recents doesn't restore
         WindowContainerTransaction commitWCT = new WindowContainerTransaction();
         mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
-                mock(SurfaceControl.Transaction.class));
+                mock(SurfaceControl.Transaction.class), mock(TransitionInfo.class));
         assertFalse(mStageCoordinator.isSplitScreenVisible());
     }
 
@@ -317,7 +317,7 @@
         mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
         mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
         mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
-                mock(SurfaceControl.Transaction.class));
+                mock(SurfaceControl.Transaction.class), mock(TransitionInfo.class));
         assertTrue(mStageCoordinator.isSplitScreenVisible());
     }
 
@@ -407,7 +407,8 @@
         TransitionInfo enterInfo = createEnterPairInfo();
         IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null);
+                new RemoteTransition(new TestRemoteTransition(), "Test"),
+                mStageCoordinator, null, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 60d6978..5cd548b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -277,7 +277,7 @@
         IBinder transitToken = new Binder();
         transitions.requestStartTransition(transitToken,
                 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */,
-                        new RemoteTransition(testRemote)));
+                        new RemoteTransition(testRemote, "Test")));
         verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
         TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
@@ -422,7 +422,7 @@
                 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
         filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
 
-        transitions.registerRemote(filter, new RemoteTransition(testRemote));
+        transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test"));
         mMainExecutor.flushAll();
 
         IBinder transitToken = new Binder();
@@ -466,11 +466,12 @@
         final int transitType = TRANSIT_FIRST_CUSTOM + 1;
 
         OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor,
-                new RemoteTransition(testRemote));
+                new RemoteTransition(testRemote, "Test"));
         // Verify that it responds to the remote but not other things.
         IBinder transitToken = new Binder();
         assertNotNull(oneShot.handleRequest(transitToken,
-                new TransitionRequestInfo(transitType, null, new RemoteTransition(testRemote))));
+                new TransitionRequestInfo(transitType, null,
+                        new RemoteTransition(testRemote, "Test"))));
         assertNull(oneShot.handleRequest(transitToken,
                 new TransitionRequestInfo(transitType, null, null)));
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index c92d2f3..dfa3c10 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -583,7 +583,7 @@
             int cornerRadius = loadDimensionPixelSize(resources, mCaptionMenuCornerRadiusId);
             String name = "Test Window";
             WindowDecoration.AdditionalWindow additionalWindow =
-                    addWindow(R.layout.desktop_mode_decor_handle_menu, name,
+                    addWindow(R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, name,
                             mMockSurfaceControlAddWindowT,
                             x - mRelayoutResult.mDecorContainerOffsetX,
                             y - mRelayoutResult.mDecorContainerOffsetY,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3b12972..5d79104 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -738,6 +738,9 @@
         "tests/unit/VectorDrawableTests.cpp",
         "tests/unit/WebViewFunctorManagerTests.cpp",
     ],
+    data: [
+        ":hwuimicro",
+    ],
 }
 
 // ------------------------
diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
index 768dfcd..706f18c 100644
--- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -85,28 +85,20 @@
 }
 
 static SkMatrix createMatrixFromBufferTransform(SkScalar width, SkScalar height, int transform) {
-    auto matrix = SkMatrix();
     switch (transform) {
         case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
-            matrix.setRotate(90);
-            matrix.postTranslate(width, 0);
-            break;
+            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
         case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
-            matrix.setRotate(180);
-            matrix.postTranslate(width, height);
-            break;
+            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
         case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
-            matrix.setRotate(270);
-            matrix.postTranslate(0, width);
-            break;
+            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
         default:
             ALOGE("Invalid transform provided. Transform should be validated from"
                   "the java side. Leveraging identity transform as a fallback");
             [[fallthrough]];
         case ANATIVEWINDOW_TRANSFORM_IDENTITY:
-            break;
+            return SkMatrix::I();
     }
-    return matrix;
 }
 
 static int android_graphics_HardwareBufferRenderer_render(JNIEnv* env, jobject, jlong renderProxy,
@@ -117,8 +109,8 @@
     auto skHeight = static_cast<SkScalar>(height);
     auto matrix = createMatrixFromBufferTransform(skWidth, skHeight, transform);
     auto colorSpace = GraphicsJNI::getNativeColorSpace(colorspacePtr);
-    proxy->setHardwareBufferRenderParams(
-            HardwareBufferRenderParams(matrix, colorSpace, createRenderCallback(env, consumer)));
+    proxy->setHardwareBufferRenderParams(HardwareBufferRenderParams(
+            width, height, matrix, colorSpace, createRenderCallback(env, consumer)));
     nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
     UiFrameInfoBuilder(proxy->frameInfo())
                 .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID,
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 202a62c..cc987bc 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -69,15 +69,9 @@
 }
 
 Frame SkiaOpenGLPipeline::getFrame() {
-    if (mHardwareBuffer) {
-        AHardwareBuffer_Desc description;
-        AHardwareBuffer_describe(mHardwareBuffer, &description);
-        return Frame(description.width, description.height, 0);
-    } else {
-        LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-                            "drawRenderNode called on a context with no surface!");
-        return mEglManager.beginFrame(mEglSurface);
-    }
+    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+                        "drawRenderNode called on a context with no surface!");
+    return mEglManager.beginFrame(mEglSurface);
 }
 
 IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 99298bc..c8f2e69 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -66,15 +66,8 @@
 }
 
 Frame SkiaVulkanPipeline::getFrame() {
-    if (mHardwareBuffer) {
-        AHardwareBuffer_Desc description;
-        AHardwareBuffer_describe(mHardwareBuffer, &description);
-        return Frame(description.width, description.height, 0);
-    } else {
-        LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
-                            "getFrame() called on a context with no surface!");
-        return vulkanManager().dequeueNextBuffer(mVkSurface);
-    }
+    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!");
+    return vulkanManager().dequeueNextBuffer(mVkSurface);
 }
 
 IRenderPipeline::DrawResult SkiaVulkanPipeline::draw(
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index dd781bb..6b2c995 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -528,6 +528,14 @@
     sendLoadResetHint();
 }
 
+Frame CanvasContext::getFrame() {
+    if (mHardwareBuffer != nullptr) {
+        return {mBufferParams.getLogicalWidth(), mBufferParams.getLogicalHeight(), 0};
+    } else {
+        return mRenderPipeline->getFrame();
+    }
+}
+
 void CanvasContext::draw() {
     if (auto grContext = getGrContext()) {
         if (grContext->abandoned()) {
@@ -569,7 +577,8 @@
 
     mCurrentFrameInfo->markIssueDrawCommandsStart();
 
-    Frame frame = mRenderPipeline->getFrame();
+    Frame frame = getFrame();
+
     SkRect windowDirty = computeDirtyRect(frame, &dirty);
 
     ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index b26c018..3f25339 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -264,6 +264,8 @@
 
     FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
 
+    Frame getFrame();
+
     // The same type as Frame.mWidth and Frame.mHeight
     int32_t mLastFrameWidth = 0;
     int32_t mLastFrameHeight = 0;
diff --git a/libs/hwui/renderthread/HardwareBufferRenderParams.h b/libs/hwui/renderthread/HardwareBufferRenderParams.h
index 91fe3f6..8c942d0 100644
--- a/libs/hwui/renderthread/HardwareBufferRenderParams.h
+++ b/libs/hwui/renderthread/HardwareBufferRenderParams.h
@@ -36,9 +36,12 @@
 class HardwareBufferRenderParams {
 public:
     HardwareBufferRenderParams() = default;
-    HardwareBufferRenderParams(const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
+    HardwareBufferRenderParams(int32_t logicalWidth, int32_t logicalHeight,
+                               const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
                                RenderCallback&& callback)
-            : mTransform(transform)
+            : mLogicalWidth(logicalWidth)
+            , mLogicalHeight(logicalHeight)
+            , mTransform(transform)
             , mColorSpace(colorSpace)
             , mRenderCallback(std::move(callback)) {}
     const SkMatrix& getTransform() const { return mTransform; }
@@ -50,7 +53,12 @@
         }
     }
 
+    int32_t getLogicalWidth() { return mLogicalWidth; }
+    int32_t getLogicalHeight() { return mLogicalHeight; }
+
 private:
+    int32_t mLogicalWidth;
+    int32_t mLogicalHeight;
     SkMatrix mTransform = SkMatrix::I();
     sk_sp<SkColorSpace> mColorSpace = SkColorSpace::MakeSRGB();
     RenderCallback mRenderCallback = nullptr;
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f5a9850..9be7728 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -831,7 +831,9 @@
      * will be present for any location.
      *
      * <ul>
-     * <li> satellites - the number of satellites used to derive a GNSS fix
+     * <li> satellites - the number of satellites used to derive a GNSS fix. This key was deprecated
+     * in API 34 because the information can be obtained through more accurate means, such as by
+     * referencing {@link GnssStatus#usedInFix}.
      * </ul>
      */
     public @Nullable Bundle getExtras() {
diff --git a/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
index dcf3945..47d8426 100644
--- a/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
@@ -25,14 +25,14 @@
 oneway interface IInjectGlobalEvent {
 
     /**
-     * Request a fake STHAL restart.
+     * Trigger a fake STHAL restart.
      * This invalidates the {@link IInjectGlobalEvent}.
      */
     void triggerRestart();
 
     /**
-     * Triggers global resource contention into the fake STHAL. Loads/startRecognition
-     * will fail with RESOURCE_CONTENTION.
+     * Set global resource contention within the fake STHAL. Loads/startRecognition
+     * will fail with {@code RESOURCE_CONTENTION} until unset.
      * @param isContended - true to enable resource contention. false to disable resource contention
      *                      and resume normal functionality.
      * @param callback - Call {@link IAcknowledgeEvent#eventReceived()} on this interface once
@@ -40,4 +40,11 @@
      */
     void setResourceContention(boolean isContended, IAcknowledgeEvent callback);
 
+    /**
+     * Trigger an
+     * {@link android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback#onResourcesAvailable}
+     * callback from the fake STHAL. This callback is used to signal to the framework that
+     * previous operations which failed may now succeed.
+     */
+    void triggerOnResourcesAvailable();
 }
diff --git a/media/aidl/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS
index 01b2cb9..1e41886 100644
--- a/media/aidl/android/media/soundtrigger_middleware/OWNERS
+++ b/media/aidl/android/media/soundtrigger_middleware/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 293d3d2..e73cf87 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -70,7 +70,7 @@
         throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
     }
 
-    /* These values must be kept in sync with system/audio.h */
+    /* These values must be kept in sync with system/media/audio/include/system/audio-hal-enums.h */
     /*
      * If these are modified, please also update Settings.System.VOLUME_SETTINGS
      * and attrs.xml and AudioManager.java.
@@ -961,7 +961,8 @@
      */
 
     //
-    // audio device definitions: must be kept in sync with values in system/core/audio.h
+    // audio device definitions: must be kept in sync with values
+    // in system/media/audio/include/system/audio-hal-enums.h
     //
     /** @hide */
     public static final int DEVICE_NONE = 0x0;
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index 01b2cb9..85f7a4d 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 48436
 atneya@google.com
 elaurent@google.com
diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp
index 528ac12..ebd7533 100644
--- a/native/android/tests/activitymanager/nativeTests/Android.bp
+++ b/native/android/tests/activitymanager/nativeTests/Android.bp
@@ -45,4 +45,7 @@
     required: [
         "UidImportanceHelperApp",
     ],
+    data: [
+        ":UidImportanceHelperApp",
+    ],
 }
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 82e5a7f..d87abb9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -20,7 +20,7 @@
     <string name="app_label">Companion Device Manager</string>
 
     <!-- Title of the device association confirmation dialog. -->
-    <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
+    <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;?</string>
 
     <!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
 
@@ -31,10 +31,10 @@
     <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%2$s</xliff:g>&lt;/strong&gt;</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile (type) [CHAR LIMIT=NONE] -->
-    <string name="summary_watch">The app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string>
+    <string name="summary_watch">This app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_watch_single_device">The app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, and access these permissions:</string>
+    <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_GLASSES ================= -->
 
@@ -48,7 +48,7 @@
     <string name="summary_glasses_multi_device">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your phone:</string>
+    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
 
@@ -97,10 +97,10 @@
     <string name="profile_name_generic">device</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of unspecified profile (type) [CHAR LIMIT=NONE] -->
-    <string name="summary_generic_single_device">This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>.</string>
+    <string name="summary_generic_single_device">This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="device_name" example="My Watch">%1$s</xliff:g></string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of unspecified profile (type) [CHAR LIMIT=NONE] -->
-    <string name="summary_generic">This app will be able to sync info, like the name of someone calling, between your phone and the chosen device.</string>
+    <string name="summary_generic">This app will be able to sync info, like the name of someone calling, between your phone and the chosen device</string>
 
     <!-- ================= Buttons ================= -->
 
@@ -194,4 +194,10 @@
     <!-- Description of nearby_device_streaming permission of corresponding profile [CHAR LIMIT=NONE] -->
     <string name="permission_nearby_device_streaming_summary">Stream apps and other system features from your phone</string>
 
+    <!-- The type of the device for phone [CHAR LIMIT=30] -->
+    <string name="device_type" product="default">phone</string>
+
+    <!-- The type of the device for tablet [CHAR LIMIT=30] -->
+    <string name="device_type" product="tablet">tablet</string>
+
 </resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 71ae578..4154029 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -551,8 +551,8 @@
             summary = getHtmlFromResources(this, SUMMARIES.get(null), deviceName);
             mConstraintList.setVisibility(View.GONE);
         } else {
-            summary = getHtmlFromResources(this, SUMMARIES.get(deviceProfile),
-                    getString(PROFILES_NAME.get(deviceProfile)), appLabel);
+            summary = getHtmlFromResources(
+                    this, SUMMARIES.get(deviceProfile), getString(R.string.device_type));
             mPermissionTypes.addAll(PERMISSION_TYPES.get(deviceProfile));
             setupPermissionList();
         }
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 0498a15..e9b2e10 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -42,7 +42,7 @@
   <!-- Title for subsection of "Learn more about passkeys" screen about seamless transition. [CHAR LIMIT=80] -->
   <string name="seamless_transition_title">Seamless transition</string>
   <!-- Detail for subsection of "Learn more about passkeys" screen about seamless transition. [CHAR LIMIT=500] -->
-  <string name="seamless_transition_detail">As we move towards a passwordless future, passwords will still be available alongside passkeys</string>
+  <string name="seamless_transition_detail">As we move towards a passwordless future, passwords will still be available alongside passkeys.</string>
   <!-- This appears as the title of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
   <string name="choose_provider_title">Choose where to save your <xliff:g id="createTypes" example="passkeys">%1$s</xliff:g></string>
   <!-- This appears as the description body of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 7b98049..ed4cc95 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -296,7 +296,11 @@
         }
         item { Divider(thickness = 24.dp, color = Color.Transparent) }
 
-        item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) }
+        item {
+            Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                BodyMediumText(text = stringResource(R.string.choose_provider_body))
+            }
+        }
         item { Divider(thickness = 16.dp, color = Color.Transparent) }
         item {
             CredentialContainerCard {
@@ -444,8 +448,10 @@
         }
         item { Divider(thickness = 24.dp, color = Color.Transparent) }
         item {
-            BodyMediumText(text = stringResource(
-                R.string.use_provider_for_all_description, entryInfo.userProviderDisplayName))
+            Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                BodyMediumText(text = stringResource(
+                    R.string.use_provider_for_all_description, entryInfo.userProviderDisplayName))
+            }
         }
         item { Divider(thickness = 24.dp, color = Color.Transparent) }
         item {
@@ -626,25 +632,33 @@
             MoreAboutPasskeySectionHeader(
                 text = stringResource(R.string.passwordless_technology_title)
             )
-            BodyMediumText(text = stringResource(R.string.passwordless_technology_detail))
+            Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                BodyMediumText(text = stringResource(R.string.passwordless_technology_detail))
+            }
         }
         item {
             MoreAboutPasskeySectionHeader(
                 text = stringResource(R.string.public_key_cryptography_title)
             )
-            BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail))
+            Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail))
+            }
         }
         item {
             MoreAboutPasskeySectionHeader(
                 text = stringResource(R.string.improved_account_security_title)
             )
-            BodyMediumText(text = stringResource(R.string.improved_account_security_detail))
+            Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                BodyMediumText(text = stringResource(R.string.improved_account_security_detail))
+            }
         }
         item {
             MoreAboutPasskeySectionHeader(
                 text = stringResource(R.string.seamless_transition_title)
             )
-            BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
+            Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
+                BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
+            }
         }
     }
     onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a3d632c..e884cf8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -305,10 +305,11 @@
         synchronized (mProfileLock) {
             if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                 for (CachedBluetoothDevice member : getMemberDevice()) {
-                    Log.d(TAG, "Disconnect the member(" + member.getAddress() + ")");
+                    Log.d(TAG, "Disconnect the member:" + member);
                     member.disconnect();
                 }
             }
+            Log.d(TAG, "Disconnect " + this);
             mDevice.disconnect();
         }
         // Disconnect  PBAP server in case its connected
@@ -440,11 +441,11 @@
                 Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice);
                 return;
             }
-
+            Log.d(TAG, "connect " + this);
             mDevice.connect();
             if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                 for (CachedBluetoothDevice member : getMemberDevice()) {
-                    Log.d(TAG, "connect the member(" + member.getAddress() + ")");
+                    Log.d(TAG, "connect the member:" + member);
                     member.connect();
                 }
             }
@@ -530,7 +531,7 @@
     }
 
     // TODO: do any of these need to run async on a background thread?
-    private void fillData() {
+    void fillData() {
         updateProfiles();
         fetchActiveDevices();
         migratePhonebookPermissionChoice();
@@ -933,14 +934,15 @@
 
     @Override
     public String toString() {
-        return "CachedBluetoothDevice ("
+        return "CachedBluetoothDevice{"
                 + "anonymizedAddress="
                 + mDevice.getAnonymizedAddress()
                 + ", name="
                 + getName()
                 + ", groupId="
                 + mGroupId
-                + ")";
+                + ", member=" + mMemberDevices
+                + "}";
     }
 
     @Override
@@ -1482,6 +1484,7 @@
      * Store the member devices that are in the same coordinated set.
      */
     public void addMemberDevice(CachedBluetoothDevice memberDevice) {
+        Log.d(TAG, this + " addMemberDevice = " + memberDevice);
         mMemberDevices.add(memberDevice);
     }
 
@@ -1511,13 +1514,14 @@
         mDevice = newMainDevice.mDevice;
         mRssi = newMainDevice.mRssi;
         mJustDiscovered = newMainDevice.mJustDiscovered;
+        fillData();
 
         // Set sub device from backup
         newMainDevice.release();
         newMainDevice.mDevice = tmpDevice;
         newMainDevice.mRssi = tmpRssi;
         newMainDevice.mJustDiscovered = tmpJustDiscovered;
-        fetchActiveDevices();
+        newMainDevice.fillData();
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 20a6cd8..356bb82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -71,7 +71,7 @@
                 return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
             }
 
-            for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+            for (Map.Entry<Integer, ParcelUuid> entry : groupIdMap.entrySet()) {
                 if (entry.getValue().equals(BluetoothUuid.CAP)) {
                     return entry.getKey();
                 }
@@ -153,72 +153,13 @@
             return;
         }
         log("onGroupIdChanged: mCachedDevices list =" + mCachedDevices.toString());
-        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
-        final CachedBluetoothDeviceManager deviceManager = mBtManager.getCachedDeviceManager();
-        final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
-        final BluetoothDevice mainBluetoothDevice = (leAudioProfile != null && isAtLeastT()) ?
-                leAudioProfile.getConnectedGroupLeadDevice(groupId) : null;
+        List<CachedBluetoothDevice> memberDevicesList = getMemberDevicesList(groupId);
         CachedBluetoothDevice newMainDevice =
-                mainBluetoothDevice != null ? deviceManager.findDevice(mainBluetoothDevice) : null;
-        if (newMainDevice != null) {
-            final CachedBluetoothDevice finalNewMainDevice = newMainDevice;
-            final List<CachedBluetoothDevice> memberDevices = mCachedDevices.stream()
-                    .filter(cachedDevice -> !cachedDevice.equals(finalNewMainDevice)
-                            && cachedDevice.getGroupId() == groupId)
-                    .collect(Collectors.toList());
-            if (memberDevices == null || memberDevices.isEmpty()) {
-                log("onGroupIdChanged: There is no member device in list.");
-                return;
-            }
-            log("onGroupIdChanged: removed from UI device =" + memberDevices
-                    + ", with groupId=" + groupId + " mainDevice= " + newMainDevice);
-            for (CachedBluetoothDevice memberDeviceItem : memberDevices) {
-                Set<CachedBluetoothDevice> memberSet = memberDeviceItem.getMemberDevice();
-                if (!memberSet.isEmpty()) {
-                    log("onGroupIdChanged: Transfer the member list into new main device.");
-                    for (CachedBluetoothDevice memberListItem : memberSet) {
-                        if (!memberListItem.equals(newMainDevice)) {
-                            newMainDevice.addMemberDevice(memberListItem);
-                        }
-                    }
-                    memberSet.clear();
-                }
+                getPreferredMainDeviceWithoutConectionState(groupId, memberDevicesList);
 
-                newMainDevice.addMemberDevice(memberDeviceItem);
-                mCachedDevices.remove(memberDeviceItem);
-                mBtManager.getEventManager().dispatchDeviceRemoved(memberDeviceItem);
-            }
-
-            if (!mCachedDevices.contains(newMainDevice)) {
-                mCachedDevices.add(newMainDevice);
-                mBtManager.getEventManager().dispatchDeviceAdded(newMainDevice);
-            }
-        } else {
-            log("onGroupIdChanged: There is no main device from the LE profile.");
-            int firstMatchedIndex = -1;
-
-            for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
-                final CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-                if (cachedDevice.getGroupId() != groupId) {
-                    continue;
-                }
-
-                if (firstMatchedIndex == -1) {
-                    // Found the first one
-                    firstMatchedIndex = i;
-                    newMainDevice = cachedDevice;
-                    continue;
-                }
-
-                log("onGroupIdChanged: removed from UI device =" + cachedDevice
-                        + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
-
-                newMainDevice.addMemberDevice(cachedDevice);
-                mCachedDevices.remove(i);
-                mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
-                break;
-            }
-        }
+        log("onGroupIdChanged: The mainDevice= " + newMainDevice
+                + " and the memberDevicesList of groupId= " + groupId + " =" + memberDevicesList);
+        addMemberDevicesIntoMainDevice(memberDevicesList, newMainDevice);
     }
 
     // @return {@code true}, the event is processed inside the method. It is for updating
@@ -263,7 +204,7 @@
                     break;
                 }
 
-                for (CachedBluetoothDevice device: memberSet) {
+                for (CachedBluetoothDevice device : memberSet) {
                     if (device.isConnected()) {
                         log("set device: " + device + " as the main device");
                         // Main device is disconnected and sub device is connected
@@ -296,7 +237,7 @@
                     continue;
                 }
 
-                for (CachedBluetoothDevice memberDevice: memberSet) {
+                for (CachedBluetoothDevice memberDevice : memberSet) {
                     if (memberDevice != null && memberDevice.equals(device)) {
                         return cachedDevice;
                     }
@@ -310,7 +251,6 @@
      * Check if the {@code groupId} is existed.
      *
      * @param groupId The group id
-     *
      * @return {@code true}, if we could find a device with this {@code groupId}; Otherwise,
      * return {@code false}.
      */
@@ -322,6 +262,116 @@
         return false;
     }
 
+    private List<CachedBluetoothDevice> getMemberDevicesList(int groupId) {
+        return mCachedDevices.stream()
+                .filter(cacheDevice -> cacheDevice.getGroupId() == groupId)
+                .collect(Collectors.toList());
+    }
+
+    private CachedBluetoothDevice getPreferredMainDeviceWithoutConectionState(int groupId,
+            List<CachedBluetoothDevice> memberDevicesList) {
+        // First, priority connected lead device from LE profile
+        // Second, the DUAL mode device which has A2DP/HFP and LE audio
+        // Last, any one of LE device in the list.
+        if (memberDevicesList == null || memberDevicesList.isEmpty()) {
+            return null;
+        }
+
+        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+        final CachedBluetoothDeviceManager deviceManager = mBtManager.getCachedDeviceManager();
+        final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
+        final BluetoothDevice mainBluetoothDevice = (leAudioProfile != null && isAtLeastT())
+                ? leAudioProfile.getConnectedGroupLeadDevice(groupId) : null;
+
+        if (mainBluetoothDevice != null) {
+            log("getPreferredMainDevice: The LeadDevice from LE profile is "
+                    + mainBluetoothDevice.getAnonymizedAddress());
+        }
+
+        // 1st
+        CachedBluetoothDevice newMainDevice =
+                mainBluetoothDevice != null ? deviceManager.findDevice(mainBluetoothDevice) : null;
+        if (newMainDevice != null) {
+            if (newMainDevice.isConnected()) {
+                log("getPreferredMainDevice: The connected LeadDevice from LE profile");
+                return newMainDevice;
+            } else {
+                log("getPreferredMainDevice: The LeadDevice is not connect.");
+            }
+        } else {
+            log("getPreferredMainDevice: The LeadDevice is not in the all of devices list");
+        }
+
+        // 2nd
+        newMainDevice = memberDevicesList.stream()
+                .filter(cachedDevice -> cachedDevice.getConnectableProfiles().stream()
+                        .anyMatch(profile -> profile instanceof A2dpProfile
+                                || profile instanceof HeadsetProfile))
+                .findFirst().orElse(null);
+        if (newMainDevice != null) {
+            log("getPreferredMainDevice: The DUAL mode device");
+            return newMainDevice;
+        }
+
+        // last
+        if (!memberDevicesList.isEmpty()) {
+            newMainDevice = memberDevicesList.get(0);
+        }
+        return newMainDevice;
+    }
+
+    private void addMemberDevicesIntoMainDevice(List<CachedBluetoothDevice> memberDevicesList,
+            CachedBluetoothDevice newMainDevice) {
+        if (newMainDevice == null) {
+            log("addMemberDevicesIntoMainDevice: No main device. Do nothing.");
+            return;
+        }
+        if (memberDevicesList.isEmpty()) {
+            log("addMemberDevicesIntoMainDevice: No member device in list. Do nothing.");
+            return;
+        }
+        CachedBluetoothDevice mainDeviceOfNewMainDevice = findMainDevice(newMainDevice);
+        boolean isMemberInOtherMainDevice = mainDeviceOfNewMainDevice != null;
+        if (!memberDevicesList.contains(newMainDevice) && isMemberInOtherMainDevice) {
+            log("addMemberDevicesIntoMainDevice: The 'new main device' is not in list, and it is "
+                    + "the member at other device. Do switch main and member.");
+            // To switch content and dispatch to notify UI change
+            mBtManager.getEventManager().dispatchDeviceRemoved(mainDeviceOfNewMainDevice);
+            mainDeviceOfNewMainDevice.switchMemberDeviceContent(newMainDevice);
+            mainDeviceOfNewMainDevice.refresh();
+            // It is necessary to do remove and add for updating the mapping on
+            // preference and device
+            mBtManager.getEventManager().dispatchDeviceAdded(mainDeviceOfNewMainDevice);
+        } else {
+            log("addMemberDevicesIntoMainDevice: Set new main device");
+            for (CachedBluetoothDevice memberDeviceItem : memberDevicesList) {
+                if (memberDeviceItem.equals(newMainDevice)) {
+                    continue;
+                }
+                Set<CachedBluetoothDevice> memberSet = memberDeviceItem.getMemberDevice();
+                if (!memberSet.isEmpty()) {
+                    for (CachedBluetoothDevice memberSetItem : memberSet) {
+                        if (!memberSetItem.equals(newMainDevice)) {
+                            newMainDevice.addMemberDevice(memberSetItem);
+                        }
+                    }
+                    memberSet.clear();
+                }
+
+                newMainDevice.addMemberDevice(memberDeviceItem);
+                mCachedDevices.remove(memberDeviceItem);
+                mBtManager.getEventManager().dispatchDeviceRemoved(memberDeviceItem);
+            }
+
+            if (!mCachedDevices.contains(newMainDevice)) {
+                mCachedDevices.add(newMainDevice);
+                mBtManager.getEventManager().dispatchDeviceAdded(newMainDevice);
+            }
+        }
+        log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
+                + mCachedDevices);
+    }
+
     private void log(String msg) {
         if (DEBUG) {
             Log.d(TAG, msg);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 1c179f8..6444f3b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1150,9 +1150,11 @@
         assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
         assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
         assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+        verify(mCachedDevice).fillData();
         assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
         assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
         assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+        verify(mSubCachedDevice).fillData();
         assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
new file mode 100644
index 0000000..0b71816
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -0,0 +1 @@
+per-file WritableNamespacePrefixes.java = cbrubaker@google.com,tedbauer@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5a8c594..3e1b597 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2323,7 +2323,15 @@
             return;
         } else if (hasAllowlistPermission) {
             for (String flag : flags) {
-                if (!DeviceConfig.getAdbWritableFlags().contains(flag)) {
+                boolean namespaceAllowed = false;
+                for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
+                    if (flag.startsWith(allowlistedPrefix)) {
+                        namespaceAllowed = true;
+                        break;
+                    }
+                }
+
+                if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
                     throw new SecurityException("Permission denial for flag '"
                         + flag
                         + "'; allowlist permission granted, but must add flag to the allowlist.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
new file mode 100644
index 0000000..28f25e0
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of prefixes for namespaces in which any flag can be written with adb.
+ * <p>
+ * A security review is required for any prefix that's added to this list. To add to
+ * the list, create a change and tag the OWNER. In the change description, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * allowlisted.
+ */
+final class WritableNamespacePrefixes {
+    public static final Set<String> ALLOWLIST =
+            new ArraySet<String>(Arrays.asList(
+                "app_compat_overrides",
+                "game_overlay",
+                "namespace1"
+            ));
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ac75cc8..3007d4a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -182,7 +182,6 @@
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
-        "androidx.test.ext.junit",
         "com.google.android.material_material",
         "kotlinx_coroutines_android",
         "kotlinx_coroutines",
@@ -191,6 +190,7 @@
         "SystemUI-proto",
         "monet",
         "dagger2",
+        "jsr305",
         "jsr330",
         "lottie",
         "LowLightDreamLib",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 91cb4ba..462c90b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -56,6 +56,7 @@
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:paddingStart="16dp"
+      android:paddingEnd="16dp"
       android:gravity="center_vertical"
       android:textColor="@color/colorControlNormal"
       android:textSize="@dimen/label_text_size"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 4b6f9a4..02d279f 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -24,7 +24,9 @@
 import android.os.Bundle;
 import android.provider.Browser;
 import android.provider.Settings;
+import android.view.View;
 
+import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceFragmentCompat;
@@ -56,6 +58,13 @@
             initializeHelpAndFeedbackPreference();
         }
 
+        @Override
+        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+            view.setLayoutDirection(
+                    view.getResources().getConfiguration().getLayoutDirection());
+        }
+
         /**
          * Returns large buttons settings state.
          *
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 a25790a..5b7bbe8 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
@@ -194,13 +194,15 @@
     /** Updates a11y menu layout position by configuring layout params. */
     private void updateLayoutPosition() {
         final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
-        final int orientation = mService.getResources().getConfiguration().orientation;
+        final Configuration configuration = mService.getResources().getConfiguration();
+        final int orientation = configuration.orientation;
         if (display != null && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            final boolean ltr = configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
             switch (display.getRotation()) {
-                case Surface.ROTATION_90:
+                case Surface.ROTATION_0:
                 case Surface.ROTATION_180:
                     mLayoutParameter.gravity =
-                            Gravity.END | Gravity.BOTTOM
+                            (ltr ? Gravity.END : Gravity.START) | Gravity.BOTTOM
                                     | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
                     mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT;
                     mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -208,10 +210,10 @@
                     mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
                     mLayout.setBackgroundResource(R.drawable.shadow_90deg);
                     break;
-                case Surface.ROTATION_0:
+                case Surface.ROTATION_90:
                 case Surface.ROTATION_270:
                     mLayoutParameter.gravity =
-                            Gravity.START | Gravity.BOTTOM
+                            (ltr ? Gravity.START : Gravity.END) | Gravity.BOTTOM
                                     | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
                     mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT;
                     mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT;
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 03e1e66..197b217 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -384,8 +384,15 @@
         }
 
         @JvmStatic
-        fun adaptRemoteAnimation(adapter: RemoteAnimationAdapter): RemoteTransition {
-            return RemoteTransition(adaptRemoteRunner(adapter.runner), adapter.callingApplication)
+        fun adaptRemoteAnimation(
+            adapter: RemoteAnimationAdapter,
+            debugName: String
+        ): RemoteTransition {
+            return RemoteTransition(
+                adaptRemoteRunner(adapter.runner),
+                adapter.callingApplication,
+                debugName
+            )
         }
     }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index e73afe7..a7e95b5 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.util.Assert
+import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -41,6 +42,18 @@
 private const val DEBUG = true
 private val KEY_TIMESTAMP = "appliedTimestamp"
 
+private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
+    key: TKey,
+    value: TVal,
+    onNew: () -> Unit
+): TVal {
+    val result = this.putIfAbsent(key, value)
+    if (result == null) {
+        onNew()
+    }
+    return result ?: value
+}
+
 /** ClockRegistry aggregates providers and plugins */
 open class ClockRegistry(
     val context: Context,
@@ -64,7 +77,7 @@
         fun onAvailableClocksChanged() {}
     }
 
-    private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
+    private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
     private val clockChangeListeners = mutableListOf<ClockChangeListener>()
     private val settingObserver =
         object : ContentObserver(null) {
@@ -92,18 +105,12 @@
                 var isClockListChanged = false
                 for (clock in plugin.getClocks()) {
                     val id = clock.clockId
-                    var isNew = false
                     val info =
-                        availableClocks.getOrPut(id) {
-                            isNew = true
-                            ClockInfo(clock, plugin, manager)
+                        availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
+                            isClockListChanged = true
+                            onConnected(id)
                         }
 
-                    if (isNew) {
-                        isClockListChanged = true
-                        onConnected(id)
-                    }
-
                     if (manager != info.manager) {
                         Log.e(
                             TAG,
@@ -254,10 +261,8 @@
             return
         }
 
-        android.util.Log.e("HAWK", "triggerOnCurrentClockChanged")
         scope.launch(mainDispatcher) {
             assertMainThread()
-            android.util.Log.e("HAWK", "isClockChanged")
             isClockChanged.set(false)
             clockChangeListeners.forEach { it.onCurrentClockChanged() }
         }
@@ -270,10 +275,8 @@
             return
         }
 
-        android.util.Log.e("HAWK", "triggerOnAvailableClocksChanged")
         scope.launch(mainDispatcher) {
             assertMainThread()
-            android.util.Log.e("HAWK", "isClockListChanged")
             isClockListChanged.set(false)
             clockChangeListeners.forEach { it.onAvailableClocksChanged() }
         }
@@ -356,7 +359,7 @@
     }
 
     private var isVerifying = AtomicBoolean(false)
-    private fun verifyLoadedProviders() {
+    fun verifyLoadedProviders() {
         val shouldSchedule = isVerifying.compareAndSet(false, true)
         if (!shouldSchedule) {
             return
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b49afee..4b94707 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -28,9 +28,10 @@
     <FrameLayout
         android:id="@+id/lockscreen_clock_view"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/small_clock_height"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
+        android:clipChildren="false"
         android:paddingStart="@dimen/clock_padding_start"
         android:visibility="invisible" />
     <FrameLayout
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 1f44f05..cad2c16 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -95,6 +95,7 @@
     <dimen name="num_pad_key_margin_end">12dp</dimen>
 
     <!-- additional offset for clock switch area items -->
+    <dimen name="small_clock_height">114dp</dimen>
     <dimen name="clock_padding_start">28dp</dimen>
     <dimen name="below_clock_padding_start">32dp</dimen>
     <dimen name="below_clock_padding_end">16dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 11b4d79..2143fc4 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -21,12 +21,21 @@
     <!-- Instructions telling the user to enter their PIN password to unlock the keyguard [CHAR LIMIT=30] -->
     <string name="keyguard_enter_your_pin">Enter your PIN</string>
 
+    <!-- Instructions telling the user to enter their PIN password to unlock the keyguard [CHAR LIMIT=26] -->
+    <string name="keyguard_enter_pin">Enter PIN</string>
+
     <!-- Instructions telling the user to enter their pattern to unlock the keyguard [CHAR LIMIT=30] -->
     <string name="keyguard_enter_your_pattern">Enter your pattern</string>
 
+    <!-- Instructions telling the user to enter their pattern to unlock the keyguard [CHAR LIMIT=26] -->
+    <string name="keyguard_enter_pattern">Draw pattern</string>
+
     <!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=30] -->
     <string name="keyguard_enter_your_password">Enter your password</string>
 
+    <!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=26] -->
+    <string name="keyguard_enter_password">Enter password</string>
+
     <!-- Shown in the lock screen when there is SIM card IO error. -->
     <string name="keyguard_sim_error_message_short">Invalid Card.</string>
 
@@ -118,11 +127,104 @@
 
     <!-- Message shown when user enters wrong pattern -->
     <string name="kg_wrong_pattern">Wrong pattern</string>
+
+    <!-- Message shown when user enters wrong pattern [CHAR LIMIT=26] -->
+    <string name="kg_wrong_pattern_try_again">Wrong pattern. Try again.</string>
+
     <!-- Message shown when user enters wrong password -->
     <string name="kg_wrong_password">Wrong password</string>
+
+    <!-- Message shown when user enters wrong pattern [CHAR LIMIT=26] -->
+    <string name="kg_wrong_password_try_again">Wrong password. Try again.</string>
+
     <!-- Message shown when user enters wrong PIN -->
     <string name="kg_wrong_pin">Wrong PIN</string>
-    <!-- Countdown message shown after too many failed unlock attempts -->
+
+    <!-- Message shown when user enters wrong PIN  [CHAR LIMIT=26] -->
+    <string name="kg_wrong_pin_try_again">Wrong PIN. Try again.</string>
+
+    <!-- Message shown when user enters wrong PIN/password/pattern below the main message, for ex: "Wrong PIN. Try again" in line 1 and the following text in line 2.  [CHAR LIMIT=52] -->
+    <string name="kg_wrong_input_try_fp_suggestion">Or unlock with fingerprint</string>
+
+    <!-- Message shown when user fingerprint is not recognized  [CHAR LIMIT=26] -->
+    <string name="kg_fp_not_recognized">Fingerprint not recognized</string>
+
+    <!-- Message shown when we want the users to try biometric auth again or use pin/pattern/password  [CHAR LIMIT=26] -->
+    <string name="bouncer_face_not_recognized">Face not recognized</string>
+
+    <!-- Message shown when we want the users to try biometric auth again or use pin/pattern/password  [CHAR LIMIT=52] -->
+    <string name="kg_bio_try_again_or_pin">Try again or enter PIN</string>
+
+    <!-- Message shown when we want the users to try biometric auth again or use pin/pattern/password  [CHAR LIMIT=52] -->
+    <string name="kg_bio_try_again_or_password">Try again or enter password</string>
+
+    <!-- Message shown when we want the users to try biometric auth again or use pin/pattern/password  [CHAR LIMIT=52] -->
+    <string name="kg_bio_try_again_or_pattern">Try again or draw pattern</string>
+
+    <!-- Message shown when we are on bouncer after temporary lockout of either face or fingerprint  [CHAR LIMIT=52] -->
+    <string name="kg_bio_too_many_attempts_pin">PIN is required after too many attempts</string>
+
+    <!-- Message shown when we are on bouncer after temporary lockout of either face or fingerprint  [CHAR LIMIT=52] -->
+    <string name="kg_bio_too_many_attempts_password">Password is required after too many attempts</string>
+
+    <!-- Message shown when we are on bouncer after temporary lockout of either face or fingerprint  [CHAR LIMIT=52] -->
+    <string name="kg_bio_too_many_attempts_pattern">Pattern is required after too many attempts</string>
+
+    <!-- Instructions when the user can unlock with PIN/password/pattern or fingerprint from bouncer. [CHAR LIMIT=26]  -->
+    <string name="kg_unlock_with_pin_or_fp">Unlock with PIN or fingerprint</string>
+
+    <!-- Instructions when the user can unlock with PIN/password/pattern or fingerprint from bouncer. [CHAR LIMIT=26]  -->
+    <string name="kg_unlock_with_password_or_fp">Unlock with password or fingerprint</string>
+
+    <!-- Instructions when the user can unlock with PIN/password/pattern or fingerprint from bouncer. [CHAR LIMIT=26]  -->
+    <string name="kg_unlock_with_pattern_or_fp">Unlock with pattern or fingerprint</string>
+
+    <!-- Message shown when we are on bouncer after Device admin requested lockdown.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_after_dpm_lock">For added security, device was locked by work policy</string>
+
+    <!-- Message shown for pin/pattern/password when we are on bouncer after user triggered lockdown.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_after_user_lockdown_pin">PIN is required after lockdown</string>
+
+    <!-- Message shown for pin/pattern/password when we are on bouncer after user triggered lockdown.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_after_user_lockdown_password">Password is required after lockdown</string>
+
+    <!-- Message shown for pin/pattern/password when we are on bouncer after user triggered lockdown.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_after_user_lockdown_pattern">Pattern is required after lockdown</string>
+
+    <!-- Message shown to prepare for an unattended update (OTA). Also known as an over-the-air (OTA) update.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_unattended_update">Update will install during inactive hours</string>
+
+    <!-- Message shown when primary authentication hasn't been used for some time.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_pin_auth_timeout">Added security required. PIN not used for a while.</string>
+
+    <!-- Message shown when primary authentication hasn't been used for some time.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_password_auth_timeout">Added security required. Password not used for a while.</string>
+
+    <!-- Message shown when primary authentication hasn't been used for some time.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_pattern_auth_timeout">Added security required. Pattern not used for a while.</string>
+
+    <!-- Message shown when device hasn't been unlocked for a while.  [CHAR LIMIT=52] -->
+    <string name="kg_prompt_auth_timeout">Added security required. Device wasn\u2019t unlocked for a while.</string>
+
+    <!-- Message shown when face unlock is not available after too many failed face authentication attempts.  [CHAR LIMIT=52] -->
+    <string name="kg_face_locked_out">Can\u2019t unlock with face. Too many attempts.</string>
+
+    <!-- Message shown when fingerprint unlock isn't available after too many failed fingerprint authentication attempts.  [CHAR LIMIT=52] -->
+    <string name="kg_fp_locked_out">Can\u2019t unlock with fingerprint. Too many attempts.</string>
+
+    <!-- Message shown when Trust Agent is disabled.  [CHAR LIMIT=52] -->
+    <string name="kg_trust_agent_disabled">Trust agent is unavailable</string>
+
+    <!-- Message shown when primary auth is locked out after too many attempts [CHAR LIMIT=52]  -->
+    <string name="kg_primary_auth_locked_out_pin">Too many attempts with incorrect PIN</string>
+
+    <!-- Message shown when primary auth is locked out after too many attempts [CHAR LIMIT=52]  -->
+    <string name="kg_primary_auth_locked_out_pattern">Too many attempts with incorrect pattern</string>
+
+    <!-- Message shown when primary auth is locked out after too many attempts [CHAR LIMIT=52]  -->
+    <string name="kg_primary_auth_locked_out_password">Too many attempts with incorrect password</string>
+
+    <!-- Countdown message shown after too many failed unlock attempts [CHAR LIMIT=26]-->
     <string name="kg_too_many_failed_attempts_countdown">{count, plural,
         =1 {Try again in # second.}
         other {Try again in # seconds.}
@@ -194,14 +296,14 @@
     <!-- Description of airplane mode -->
     <string name="airplane_mode">Airplane mode</string>
 
-    <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
+    <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=52] -->
+    <string name="kg_prompt_reason_restart_pattern">Pattern is required after device restarts</string>
 
-    <!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_restart_pin">PIN required after device restarts</string>
+    <!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=52] -->
+    <string name="kg_prompt_reason_restart_pin">PIN is required after device restarts</string>
 
-    <!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_restart_password">Password required after device restarts</string>
+    <!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=52] -->
+    <string name="kg_prompt_reason_restart_password">Password is required after device restarts</string>
 
     <!-- An explanation text that the pattern needs to be solved since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_timeout_pattern">For additional security, use pattern instead</string>
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index b9e711e..d8967d4 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -77,6 +77,29 @@
                 app:layout_constraintStart_toStartOf="parent"/>
 
             <Button
+                android:id="@+id/rearrange"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <Button
+                android:id="@+id/addControls"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:text="@string/controls_favorite_add_controls"
+                style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <Button
                 android:id="@+id/done"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0c0defa..2663ffb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1407,13 +1407,13 @@
     <dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
 
     <!-- The drag amount required for the split shade to fully expand. -->
-    <dimen name="split_shade_full_transition_distance">200dp</dimen>
+    <dimen name="split_shade_full_transition_distance">400dp</dimen>
     <!--
         The drag amount required for the scrim to fully fade in when expanding the split shade.
         Currently setting it a little longer than the full shade transition distance, to avoid
         having a state where the screen is fully black without any content showing.
     -->
-    <dimen name="split_shade_scrim_transition_distance">300dp</dimen>
+    <dimen name="split_shade_scrim_transition_distance">600dp</dimen>
 
     <dimen name="people_space_widget_radius">28dp</dimen>
     <dimen name="people_space_image_radius">20dp</dimen>
@@ -1553,6 +1553,9 @@
     <dimen name="status_bar_user_chip_end_margin">12dp</dimen>
     <dimen name="status_bar_user_chip_text_size">12sp</dimen>
 
+    <!-- System UI Dialog -->
+    <dimen name="dialog_title_text_size">24sp</dimen>
+
     <!-- Internet panel related dimensions -->
     <dimen name="internet_dialog_list_max_height">662dp</dimen>
     <!-- The height of the WiFi network in Internet panel. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 324ba02..1dd12ee 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2471,6 +2471,15 @@
     <!-- Controls management favorites screen. See other apps button [CHAR LIMIT=30] -->
     <string name="controls_favorite_see_other_apps">See other apps</string>
 
+    <!-- Controls management favorites screen. Rearrange controls button [CHAR LIMIT=30]-->
+    <string name="controls_favorite_rearrange_button">Rearrange</string>
+
+    <!-- Controls management edit screen. Add controls button [CHAR LIMIT=30]-->
+    <string name="controls_favorite_add_controls">Add controls</string>
+
+    <!-- Controls management edit screen. Return to editing button [CHAR LIMIT=30]-->
+    <string name="controls_favorite_back_to_editing">Back to editing</string>
+
     <!-- Controls management controls screen error on load message [CHAR LIMIT=NONE] -->
     <string name="controls_favorite_load_error">Controls could not be loaded. Check the <xliff:g id="app" example="System UI">%s</xliff:g> app to make sure that the app settings haven\u2019t changed.</string>
     <!-- Controls management controls screen no controls found on load message [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a3655c31..8a86fd5 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1041,7 +1041,7 @@
 
     <style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:textSize">24sp</item>
+        <item name="android:textSize">@dimen/dialog_title_text_size</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:lineHeight">32sp</item>
         <item name="android:gravity">center</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 53fab69..cab54d0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -66,7 +66,6 @@
 
 import java.io.PrintWriter;
 import java.util.Optional;
-import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -244,7 +243,12 @@
 
         mListenersRegistered = false;
 
-        mContext.unregisterReceiver(mDockedReceiver);
+        try {
+            mContext.unregisterReceiver(mDockedReceiver);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Docked receiver already unregistered", e);
+        }
+
         if (mRotationWatcherRegistered) {
             try {
                 WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 44f9d43..f094102 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -63,6 +63,7 @@
         final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
         for (int i = 0; i < info.getChanges().size(); i++) {
             TransitionInfo.Change change = info.getChanges().get(i);
+            if (TransitionUtil.isOrderOnly(change)) continue;
             if (filter.test(change)) {
                 out.add(TransitionUtil.newTarget(
                         change, info.getChanges().size() - i, info, t, leashMap));
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 58e7747..1fbf743 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
@@ -89,7 +89,7 @@
                 }
             }
         };
-        return new RemoteTransition(remote, appThread);
+        return new RemoteTransition(remote, appThread, "Recents");
     }
 
     /**
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index 31234cf..c22d689 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -43,9 +43,8 @@
         id: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ReleasedFlag {
-        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = false)
         checkForDupesAndAdd(flag)
         return flag
     }
@@ -55,7 +54,6 @@
         @BoolRes resourceId: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ResourceBooleanFlag {
         val flag =
             ResourceBooleanFlag(
@@ -63,7 +61,7 @@
                 name = name,
                 namespace = namespace,
                 resourceId = resourceId,
-                teamfood = teamfood
+                teamfood = false,
             )
         checkForDupesAndAdd(flag)
         return flag
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
index 27c5699..5502da1 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -43,9 +43,8 @@
         id: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ReleasedFlag {
-        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = false)
         flagMap[name] = flag
         return flag
     }
@@ -55,7 +54,6 @@
         @BoolRes resourceId: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ResourceBooleanFlag {
         val flag =
             ResourceBooleanFlag(
@@ -63,7 +61,7 @@
                 name = name,
                 namespace = namespace,
                 resourceId = resourceId,
-                teamfood = teamfood
+                teamfood = false,
             )
         flagMap[name] = flag
         return flag
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 9f2333d8..1980f70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -107,14 +107,7 @@
         // start fresh
         mDismissing = false;
         mView.resetPasswordText(false /* animate */, false /* announce */);
-        // if the user is currently locked out, enforce it.
-        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
-                KeyguardUpdateMonitor.getCurrentUser());
-        if (shouldLockout(deadline)) {
-            handleAttemptLockout(deadline);
-        } else {
-            resetState();
-        }
+        resetState();
     }
 
     @Override
@@ -277,7 +270,12 @@
     @Override
     public void onResume(int reason) {
         mResumed = true;
-        reset();
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+                KeyguardUpdateMonitor.getCurrentUser());
+        if (shouldLockout(deadline)) {
+            handleAttemptLockout(deadline);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index cdaed87..07333f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -390,6 +390,13 @@
             PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
                     x, props, animate);
         }
+
+    }
+
+    void updateKeyguardStatusViewOffset() {
+        // updateClockTargetRegions will call onTargetRegionChanged
+        // which will require the correct translationY property of keyguardStatusView after updating
+        mView.updateClockTargetRegions();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 68b40ab..5c56aab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -260,20 +260,18 @@
         mLockPatternView.setEnabled(true);
         mLockPatternView.clearPattern();
 
-        // if the user is currently locked out, enforce it.
-        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
-                KeyguardUpdateMonitor.getCurrentUser());
-        if (deadline != 0) {
-            handleAttemptLockout(deadline);
-        } else {
-            displayDefaultSecurityMessage();
-        }
+        displayDefaultSecurityMessage();
     }
 
     @Override
     public void onResume(int reason) {
         super.onResume(reason);
-        reset();
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+                KeyguardUpdateMonitor.getCurrentUser());
+        if (deadline != 0) {
+            handleAttemptLockout(deadline);
+        }
     }
 
     @Override
@@ -300,34 +298,38 @@
     @Override
     public void showPromptReason(int reason) {
         /// TODO: move all this logic into the MessageAreaController?
+        int resId =  0;
         switch (reason) {
             case PROMPT_REASON_RESTART:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
+                resId = R.string.kg_prompt_reason_restart_pattern;
                 break;
             case PROMPT_REASON_TIMEOUT:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_DEVICE_ADMIN:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
+                resId = R.string.kg_prompt_reason_device_admin;
                 break;
             case PROMPT_REASON_USER_REQUEST:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
+                resId = R.string.kg_prompt_reason_user_request;
                 break;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_NONE:
                 break;
             default:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
         }
+        if (resId != 0) {
+            mMessageAreaController.setMessage(getResources().getText(resId), /* animate= */ false);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1a572b7..87a7758 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -40,6 +40,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
@@ -64,6 +65,7 @@
 import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.Utils;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
@@ -634,6 +636,16 @@
                 mKeyguardStateController.isFaceAuthEnabled());
     }
 
+    /** Sets an initial message that would override the default message */
+    public void setInitialMessage() {
+        CharSequence customMessage = mViewMediatorCallback.consumeCustomMessage();
+        if (!TextUtils.isEmpty(customMessage)) {
+            showMessage(customMessage, Utils.getColorError(getContext()));
+            return;
+        }
+        showPromptReason(mViewMediatorCallback.getBouncerPromptReason());
+    }
+
     /**
      * Show the bouncer and start appear animations.
      *
@@ -753,7 +765,7 @@
                 case SimPuk:
                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
                     SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
-                    if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
+                    if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser())) {
                         finish = true;
                         eventSubtype = BOUNCER_DISMISS_SIM;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index f4c5815..fd55d69 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -172,11 +172,15 @@
      * Update position of the view with an optional animation
      */
     public void updatePosition(int x, int y, float scale, boolean animate) {
+        float oldY = mView.getY();
         PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
                 animate);
 
         mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
                 animate);
+        if (oldY != y) {
+            mKeyguardClockSwitchController.updateKeyguardStatusViewOffset();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index a678edc..ac0a3fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -28,6 +28,7 @@
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.AnimatorHandle;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -47,6 +48,7 @@
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private boolean mAnimateYPos;
     private boolean mKeyguardViewVisibilityAnimating;
+    private AnimatorHandle mKeyguardAnimatorHandle;
     private boolean mLastOccludedState = false;
     private final AnimationProperties mAnimationProperties = new AnimationProperties();
     private final LogBuffer mLogBuffer;
@@ -83,6 +85,10 @@
             boolean keyguardFadingAway,
             boolean goingToFullShade,
             int oldStatusBarState) {
+        if (mKeyguardAnimatorHandle != null) {
+            mKeyguardAnimatorHandle.cancel();
+            mKeyguardAnimatorHandle = null;
+        }
         mView.animate().cancel();
         boolean isOccluded = mKeyguardStateController.isOccluded();
         mKeyguardViewVisibilityAnimating = false;
@@ -116,7 +122,7 @@
                     .setDuration(320)
                     .setInterpolator(Interpolators.ALPHA_IN)
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
-            log("keyguardFadingAway transition w/ Y Aniamtion");
+            log("keyguardFadingAway transition w/ Y Animation");
         } else if (statusBarState == KEYGUARD) {
             if (keyguardFadingAway) {
                 mKeyguardViewVisibilityAnimating = true;
@@ -148,7 +154,7 @@
 
                 // Ask the screen off animation controller to animate the keyguard visibility for us
                 // since it may need to be cancelled due to keyguard lifecycle events.
-                mScreenOffAnimationController.animateInKeyguard(
+                mKeyguardAnimatorHandle = mScreenOffAnimationController.animateInKeyguard(
                         mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
             } else {
                 log("Direct set Visibility to VISIBLE");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1ae380e..235a8bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -22,7 +22,6 @@
 import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -127,8 +126,6 @@
     private boolean mCanDismissLockScreen;
     private int mStatusBarState;
     private boolean mIsKeyguardShowing;
-    private boolean mUserUnlockedWithBiometric;
-    private Runnable mCancelDelayedUpdateVisibilityRunnable;
     private Runnable mOnGestureDetectedRunnable;
     private Runnable mLongPressCancelRunnable;
 
@@ -229,7 +226,6 @@
         updateIsUdfpsEnrolled();
         updateConfiguration();
         updateKeyguardShowing();
-        mUserUnlockedWithBiometric = false;
 
         mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
         mIsDozing = mStatusBarStateController.isDozing();
@@ -270,11 +266,6 @@
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
         mKeyguardStateController.removeCallback(mKeyguardStateCallback);
 
-        if (mCancelDelayedUpdateVisibilityRunnable != null) {
-            mCancelDelayedUpdateVisibilityRunnable.run();
-            mCancelDelayedUpdateVisibilityRunnable = null;
-        }
-
         mAccessibilityManager.removeAccessibilityStateChangeListener(
                 mAccessibilityStateChangeListener);
     }
@@ -288,11 +279,6 @@
     }
 
     private void updateVisibility() {
-        if (mCancelDelayedUpdateVisibilityRunnable != null) {
-            mCancelDelayedUpdateVisibilityRunnable.run();
-            mCancelDelayedUpdateVisibilityRunnable = null;
-        }
-
         if (!mIsKeyguardShowing && !mIsDozing) {
             mView.setVisibility(View.INVISIBLE);
             return;
@@ -300,9 +286,9 @@
 
         boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
                 && !mShowAodUnlockedIcon && !mShowAodLockIcon;
-        mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
+        mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
                 && (!mUdfpsEnrolled || !mRunningFPS);
-        mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
+        mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
         mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
         mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
 
@@ -426,7 +412,6 @@
         pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
                 + mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
         pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
-        pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
         pw.println(" mRunningFPS: " + mRunningFPS);
         pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
@@ -469,17 +454,6 @@
         }
     }
 
-    /**
-     * @return whether the userUnlockedWithBiometric state changed
-     */
-    private boolean updateUserUnlockedWithBiometric() {
-        final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
-        mUserUnlockedWithBiometric =
-                mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                        KeyguardUpdateMonitor.getCurrentUser());
-        return wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric;
-    }
-
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
@@ -516,36 +490,15 @@
                 }
 
                 @Override
-                public void onBiometricsCleared() {
-                    if (updateUserUnlockedWithBiometric()) {
-                        updateVisibility();
-                    }
-                }
-
-                @Override
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
                     final boolean wasRunningFps = mRunningFPS;
-                    final boolean userUnlockedWithBiometricChanged =
-                            updateUserUnlockedWithBiometric();
 
                     if (biometricSourceType == FINGERPRINT) {
                         mRunningFPS = running;
-                        if (wasRunningFps && !mRunningFPS) {
-                            if (mCancelDelayedUpdateVisibilityRunnable != null) {
-                                mCancelDelayedUpdateVisibilityRunnable.run();
-                            }
-
-                            // For some devices, auth is cancelled immediately on screen off but
-                            // before dozing state is set. We want to avoid briefly showing the
-                            // button in this case, so we delay updating the visibility by 50ms.
-                            mCancelDelayedUpdateVisibilityRunnable =
-                                    mExecutor.executeDelayed(() -> updateVisibility(), 50);
-                            return;
-                        }
                     }
 
-                    if (userUnlockedWithBiometricChanged || wasRunningFps != mRunningFPS) {
+                    if (wasRunningFps != mRunningFPS) {
                         updateVisibility();
                     }
                 }
@@ -556,7 +509,6 @@
         @Override
         public void onUnlockedChanged() {
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-            updateUserUnlockedWithBiometric();
             updateKeyguardShowing();
             updateVisibility();
         }
@@ -573,9 +525,6 @@
             mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
 
             updateKeyguardShowing();
-            if (mIsKeyguardShowing) {
-                updateUserUnlockedWithBiometric();
-            }
             updateVisibility();
         }
 
@@ -694,7 +643,7 @@
 
     private void onLongPress() {
         cancelTouches();
-        if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+        if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
             Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
index 1836ce8..c9579d5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
@@ -21,6 +21,7 @@
 import android.content.res.Configuration
 import android.os.Bundle
 import android.provider.Settings
+import android.util.TypedValue
 import android.view.LayoutInflater
 import android.widget.Button
 import android.widget.SeekBar
@@ -49,8 +50,7 @@
     private lateinit var seekBarWithIconButtonsView: SeekBarWithIconButtonsView
     private var lastProgress: Int = -1
 
-    private val configuration: Configuration =
-        Configuration(context.getResources().getConfiguration())
+    private val configuration: Configuration = Configuration(context.resources.configuration)
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setTitle(R.string.font_scaling_dialog_title)
@@ -84,31 +84,45 @@
 
         seekBarWithIconButtonsView.setOnSeekBarChangeListener(
             object : OnSeekBarChangeListener {
+                var isTrackingTouch = false
+
                 override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
-                    if (progress != lastProgress) {
-                        if (!fontSizeHasBeenChangedFromTile) {
-                            backgroundExecutor.execute { updateSecureSettingsIfNeeded() }
-                            fontSizeHasBeenChangedFromTile = true
-                        }
-
-                        backgroundExecutor.execute { updateFontScale(strEntryValues[progress]) }
-
-                        lastProgress = progress
+                    if (!isTrackingTouch) {
+                        // The seekbar progress is changed by icon buttons
+                        changeFontSize(progress)
+                    } else {
+                        // Provide preview configuration for text instead of changing the system
+                        // font scale before users release their finger from the seekbar.
+                        createTextPreview(progress)
                     }
                 }
 
                 override fun onStartTrackingTouch(seekBar: SeekBar) {
-                    // Do nothing
+                    isTrackingTouch = true
                 }
 
                 override fun onStopTrackingTouch(seekBar: SeekBar) {
-                    // Do nothing
+                    isTrackingTouch = false
+                    changeFontSize(seekBar.progress)
                 }
             }
         )
         doneButton.setOnClickListener { dismiss() }
     }
 
+    private fun changeFontSize(progress: Int) {
+        if (progress != lastProgress) {
+            if (!fontSizeHasBeenChangedFromTile) {
+                backgroundExecutor.execute { updateSecureSettingsIfNeeded() }
+                fontSizeHasBeenChangedFromTile = true
+            }
+
+            backgroundExecutor.execute { updateFontScale(strEntryValues[progress]) }
+
+            lastProgress = progress
+        }
+    }
+
     private fun fontSizeValueToIndex(value: Float): Int {
         var lastValue = strEntryValues[0].toFloat()
         for (i in 1 until strEntryValues.size) {
@@ -153,6 +167,20 @@
         }
     }
 
+    /** Provides font size preview for text before putting the final settings to the system. */
+    fun createTextPreview(index: Int) {
+        val previewConfig = Configuration(configuration)
+        previewConfig.fontScale = strEntryValues[index].toFloat()
+
+        val previewConfigContext = context.createConfigurationContext(previewConfig)
+        previewConfigContext.theme.setTo(context.theme)
+
+        title.setTextSize(
+            TypedValue.COMPLEX_UNIT_PX,
+            previewConfigContext.resources.getDimension(R.dimen.dialog_title_text_size)
+        )
+    }
+
     companion object {
         private const val ON = "1"
         private const val OFF = "0"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 64211b5..d15a2af 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -39,7 +39,7 @@
     private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) =
         mainExecutor.execute {
             action?.let {
-                if (event.tracking) {
+                if (event.tracking || event.expanded) {
                     Log.v(TAG, "Detected panel interaction, event: $event")
                     it.onPanelInteraction.run()
                     disable()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index cbc0a1b..8e5c76c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -25,7 +25,6 @@
 import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
 
 import android.content.BroadcastReceiver;
@@ -983,7 +982,7 @@
         }
 
         if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
-            if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+            if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
                 Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 3e7d81a..063b41e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -390,11 +390,14 @@
             return true
         }
 
-        // Only pause auth if we're not on the keyguard AND we're not transitioning to doze
-        // (ie: dozeAmount = 0f). For the UnlockedScreenOffAnimation, the statusBarState is
+        // Only pause auth if we're not on the keyguard AND we're not transitioning to doze.
+        // For the UnlockedScreenOffAnimation, the statusBarState is
         // delayed. However, we still animate in the UDFPS affordance with the
-        // mUnlockedScreenOffDozeAnimator.
-        if (statusBarState != StatusBarState.KEYGUARD && lastDozeAmount == 0f) {
+        // unlockedScreenOffDozeAnimator.
+        if (
+            statusBarState != StatusBarState.KEYGUARD &&
+                !unlockedScreenOffAnimationController.isAnimationPlaying()
+        ) {
             return true
         }
         if (isBouncerExpansionGreaterThan(.5f)) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 701df89..334cf93 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -41,7 +41,6 @@
     public static final int SHADE_DRAG = 11;
     public static final int QS_COLLAPSE = 12;
     public static final int UDFPS_AUTHENTICATION = 13;
-    public static final int LOCK_ICON = 14;
     public static final int QS_SWIPE_SIDE = 15;
     public static final int BACK_GESTURE = 16;
     public static final int QS_SWIPE_NESTED = 17;
@@ -58,12 +57,10 @@
             GENERIC,
             BOUNCER_UNLOCK,
             PULSE_EXPAND,
-            BRIGHTNESS_SLIDER,
             SHADE_DRAG,
             QS_COLLAPSE,
             BRIGHTNESS_SLIDER,
             UDFPS_AUTHENTICATION,
-            LOCK_ICON,
             QS_SWIPE_SIDE,
             QS_SWIPE_NESTED,
             BACK_GESTURE,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index d17eadd..8ec48b9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -19,7 +19,6 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
 import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
 
 import android.provider.DeviceConfig;
@@ -73,8 +72,7 @@
         }
 
         if (interactionType == LEFT_AFFORDANCE
-                || interactionType == RIGHT_AFFORDANCE
-                || interactionType == LOCK_ICON) {
+                || interactionType == RIGHT_AFFORDANCE) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index f8ee49a..15e2e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -158,7 +158,6 @@
                 || interactionType == SHADE_DRAG
                 || interactionType == QS_COLLAPSE
                 || interactionType == Classifier.UDFPS_AUTHENTICATION
-                || interactionType == Classifier.LOCK_ICON
                 || interactionType == Classifier.QS_SWIPE_SIDE
                 || interactionType == QS_SWIPE_NESTED) {
             return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index d8d2c98..2fb6aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -47,8 +47,7 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == Classifier.UDFPS_AUTHENTICATION
-                || interactionType == Classifier.LOCK_ICON) {
+        if (interactionType == Classifier.UDFPS_AUTHENTICATION) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 840982c..4a3710b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,7 +21,6 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
 import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
@@ -93,8 +92,7 @@
             double historyBelief, double historyConfidence) {
         if (interactionType == BRIGHTNESS_SLIDER
                 || interactionType == MEDIA_SEEKBAR
-                || interactionType == SHADE_DRAG
-                || interactionType == LOCK_ICON) {
+                || interactionType == SHADE_DRAG) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e049ae0..c312f69 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -308,7 +308,7 @@
                 if (model.isSensitive()) {
                     mView.showTextPreview(mContext.getString(R.string.clipboard_asterisks), true);
                 } else {
-                    mView.showTextPreview(model.getText(), false);
+                    mView.showTextPreview(model.getText().toString(), false);
                 }
                 mView.setEditAccessibilityAction(true);
                 mOnPreviewTapped = this::editText;
@@ -527,7 +527,7 @@
     }
 
     private void showEditableText(CharSequence text, boolean hidden) {
-        mView.showTextPreview(text, hidden);
+        mView.showTextPreview(text.toString(), hidden);
         mView.setEditAccessibilityAction(true);
         mOnPreviewTapped = this::editText;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index ac1150e..e8c97bf 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,7 +31,6 @@
 import android.util.ArrayMap
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID
 import com.android.systemui.Dumpable
 import com.android.systemui.backup.BackupHelper
 import com.android.systemui.controls.ControlStatus
@@ -44,7 +43,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 00a406e..be428a8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -75,9 +75,12 @@
         } else {
             favoriteIds.remove(controlId)
         }
-        if (changed && !modified) {
-            modified = true
-            controlsModelCallback.onFirstChange()
+        if (changed) {
+            if (!modified) {
+                modified = true
+                controlsModelCallback.onFirstChange()
+            }
+            controlsModelCallback.onChange()
         }
         toChange?.let {
             it.controlStatus.favorite = favorite
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 7df0865..d629e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -27,6 +27,7 @@
 import android.view.ViewStub
 import android.widget.Button
 import android.widget.TextView
+import android.widget.Toast
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
@@ -38,8 +39,9 @@
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -48,17 +50,19 @@
  * Activity for rearranging and removing controls for a given structure
  */
 open class ControlsEditingActivity @Inject constructor(
+    featureFlags: FeatureFlags,
     @Main private val mainExecutor: Executor,
     private val controller: ControlsControllerImpl,
     private val userTracker: UserTracker,
     private val customIconCache: CustomIconCache,
-    private val uiController: ControlsUiController
 ) : ComponentActivity() {
 
     companion object {
         private const val DEBUG = false
         private const val TAG = "ControlsEditingActivity"
         const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
+        const val EXTRA_APP = ControlsFavoritingActivity.EXTRA_APP
+        const val EXTRA_FROM_FAVORITING = "extra_from_favoriting"
         private val SUBTITLE_ID = R.string.controls_favorite_rearrange
         private val EMPTY_TEXT_ID = R.string.controls_favorite_removed
     }
@@ -68,7 +72,12 @@
     private lateinit var model: FavoritesModel
     private lateinit var subtitle: TextView
     private lateinit var saveButton: View
+    private lateinit var addControls: View
 
+    private var isFromFavoriting: Boolean = false
+
+    private val isNewFlowEnabled: Boolean =
+        featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
     private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
         private val startingUser = controller.currentUserId
 
@@ -93,7 +102,7 @@
         intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)?.let {
             component = it
         } ?: run(this::finish)
-
+        isFromFavoriting = intent.getBooleanExtra(EXTRA_FROM_FAVORITING, false)
         intent.getCharSequenceExtra(EXTRA_STRUCTURE)?.let {
             structure = it
         } ?: run(this::finish)
@@ -165,8 +174,42 @@
     }
 
     private fun bindButtons() {
+        addControls = requireViewById<Button>(R.id.addControls).apply {
+            isEnabled = true
+            visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+            setOnClickListener {
+                if (saveButton.isEnabled) {
+                    // The user has made changes
+                    Toast.makeText(
+                        applicationContext,
+                        R.string.controls_favorite_toast_no_changes,
+                        Toast.LENGTH_SHORT
+                    ).show()
+                }
+                if (isFromFavoriting) {
+                    animateExitAndFinish()
+                } else {
+                    startActivity(Intent(context, ControlsFavoritingActivity::class.java).also {
+                        it.putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure)
+                        it.putExtra(Intent.EXTRA_COMPONENT_NAME, component)
+                        it.putExtra(
+                            ControlsFavoritingActivity.EXTRA_APP,
+                            intent.getCharSequenceExtra(EXTRA_APP),
+                        )
+                        it.putExtra(
+                            ControlsFavoritingActivity.EXTRA_SOURCE,
+                            ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING,
+                        )
+                    },
+                                  ActivityOptions.makeSceneTransitionAnimation(
+                                      this@ControlsEditingActivity
+                                  ).toBundle(),
+                    )
+                }
+            }
+        }
         saveButton = requireViewById<Button>(R.id.done).apply {
-            isEnabled = false
+            isEnabled = isFromFavoriting
             setText(R.string.save)
             setOnClickListener {
                 saveFavorites()
@@ -194,6 +237,8 @@
             }
         }
 
+        override fun onChange() = Unit
+
         override fun onFirstChange() {
             saveButton.isEnabled = true
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 3e97d31..d3ffc95 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -37,6 +37,7 @@
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
+import androidx.annotation.VisibleForTesting
 import androidx.viewpager2.widget.ViewPager2
 import com.android.systemui.Prefs
 import com.android.systemui.R
@@ -45,20 +46,20 @@
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import java.text.Collator
 import java.util.concurrent.Executor
-import java.util.function.Consumer
 import javax.inject.Inject
 
 open class ControlsFavoritingActivity @Inject constructor(
+    featureFlags: FeatureFlags,
     @Main private val executor: Executor,
     private val controller: ControlsControllerImpl,
     private val listingController: ControlsListingController,
     private val userTracker: UserTracker,
-    private val uiController: ControlsUiController
 ) : ComponentActivity() {
 
     companion object {
@@ -71,7 +72,10 @@
         // If provided, show this structure page first
         const val EXTRA_STRUCTURE = "extra_structure"
         const val EXTRA_SINGLE_STRUCTURE = "extra_single_structure"
-        const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
+        const val EXTRA_SOURCE = "extra_source"
+        const val EXTRA_SOURCE_UNDEFINED: Byte = 0
+        const val EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR: Byte = 1
+        const val EXTRA_SOURCE_VALUE_FROM_EDITING: Byte = 2
         private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
         private const val TOOLTIP_MAX_SHOWN = 2
     }
@@ -79,7 +83,7 @@
     private var component: ComponentName? = null
     private var appName: CharSequence? = null
     private var structureExtra: CharSequence? = null
-    private var fromProviderSelector = false
+    private var openSource = EXTRA_SOURCE_UNDEFINED
 
     private lateinit var structurePager: ViewPager2
     private lateinit var statusText: TextView
@@ -89,12 +93,19 @@
     private var mTooltipManager: TooltipManager? = null
     private lateinit var doneButton: View
     private lateinit var otherAppsButton: View
+    private lateinit var rearrangeButton: Button
     private var listOfStructures = emptyList<StructureContainer>()
 
     private lateinit var comparator: Comparator<StructureContainer>
     private var cancelLoadRunnable: Runnable? = null
     private var isPagerLoaded = false
 
+    private val fromProviderSelector: Boolean
+        get() = openSource == EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR
+    private val fromEditing: Boolean
+        get() = openSource == EXTRA_SOURCE_VALUE_FROM_EDITING
+    private val isNewFlowEnabled: Boolean =
+        featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
     private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
         private val startingUser = controller.currentUserId
 
@@ -117,14 +128,20 @@
 
         override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
             if (serviceInfos.size > 1) {
-                otherAppsButton.post {
-                    otherAppsButton.visibility = View.VISIBLE
+                val newVisibility = if (isNewFlowEnabled) View.GONE else View.VISIBLE
+                if (otherAppsButton.visibility != newVisibility) {
+                    otherAppsButton.post {
+                        otherAppsButton.visibility = newVisibility
+                    }
                 }
             }
         }
     }
 
     override fun onBackPressed() {
+        if (fromEditing) {
+            animateExitAndFinish()
+        }
         if (!fromProviderSelector) {
             openControlsOrigin()
         }
@@ -139,7 +156,7 @@
         appName = intent.getCharSequenceExtra(EXTRA_APP)
         structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE)
         component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
-        fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
+        openSource = intent.getByteExtra(EXTRA_SOURCE, EXTRA_SOURCE_UNDEFINED)
 
         bindViews()
     }
@@ -148,14 +165,19 @@
         override fun onFirstChange() {
             doneButton.isEnabled = true
         }
+
+        override fun onChange() {
+            val structure: StructureContainer = listOfStructures[structurePager.currentItem]
+            rearrangeButton.isEnabled = structure.model.favorites.isNotEmpty()
+        }
     }
 
     private fun loadControls() {
-        component?.let {
+        component?.let { componentName ->
             statusText.text = resources.getText(com.android.internal.R.string.loading)
             val emptyZoneString = resources.getText(
                     R.string.controls_favorite_other_zone_header)
-            controller.loadForComponent(it, Consumer { data ->
+            controller.loadForComponent(componentName, { data ->
                 val allControls = data.allControls
                 val favoriteKeys = data.favoritesIds
                 val error = data.errorOnLoad
@@ -213,7 +235,7 @@
                         ControlsAnimations.enterAnimation(structurePager).start()
                     }
                 }
-            }, Consumer { runnable -> cancelLoadRunnable = runnable })
+            }, { runnable -> cancelLoadRunnable = runnable })
         }
     }
 
@@ -299,7 +321,8 @@
         bindButtons()
     }
 
-    private fun animateExitAndFinish() {
+    @VisibleForTesting
+    internal open fun animateExitAndFinish() {
         val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
         ControlsAnimations.exitAnimation(
                 rootView,
@@ -312,6 +335,32 @@
     }
 
     private fun bindButtons() {
+        rearrangeButton = requireViewById<Button>(R.id.rearrange).apply {
+            text = if (fromEditing) {
+                getString(R.string.controls_favorite_back_to_editing)
+            } else {
+                getString(R.string.controls_favorite_rearrange_button)
+            }
+            isEnabled = false
+            visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+            setOnClickListener {
+                if (component == null) return@setOnClickListener
+                saveFavorites()
+                startActivity(
+                    Intent(context, ControlsEditingActivity::class.java).also {
+                        it.putExtra(Intent.EXTRA_COMPONENT_NAME, component)
+                        it.putExtra(ControlsEditingActivity.EXTRA_APP, appName)
+                        it.putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, true)
+                        it.putExtra(
+                            ControlsEditingActivity.EXTRA_STRUCTURE,
+                            listOfStructures[structurePager.currentItem].structureName,
+                        )
+                    },
+                    ActivityOptions
+                        .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
+                )
+            }
+        }
         otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
             setOnClickListener {
                 if (doneButton.isEnabled) {
@@ -335,18 +384,22 @@
             isEnabled = false
             setOnClickListener {
                 if (component == null) return@setOnClickListener
-                listOfStructures.forEach {
-                    val favoritesForStorage = it.model.favorites
-                    controller.replaceFavoritesForStructure(
-                        StructureInfo(component!!, it.structureName, favoritesForStorage)
-                    )
-                }
+                saveFavorites()
                 animateExitAndFinish()
                 openControlsOrigin()
             }
         }
     }
 
+    private fun saveFavorites() {
+        listOfStructures.forEach {
+            val favoritesForStorage = it.model.favorites
+            controller.replaceFavoritesForStructure(
+                StructureInfo(component!!, it.structureName, favoritesForStorage)
+            )
+        }
+    }
+
     private fun openControlsOrigin() {
         startActivity(
             Intent(applicationContext, ControlsActivity::class.java),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index d65481a..3455e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -71,6 +71,11 @@
          * Use to notify that the model has changed for the first time
          */
         fun onFirstChange()
+
+        /**
+         * Use to notify that the model has changed
+         */
+        fun onChange()
     }
 
     /**
@@ -132,7 +137,7 @@
         controlInfo: ControlInfo,
         favorite: Boolean,
         customIconGetter: (ComponentName, String) -> Icon?
-    ): this(component, controlInfo, favorite) {
+    ) : this(component, controlInfo, favorite) {
         this.customIconGetter = customIconGetter
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 3808e73..92aff06 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -211,7 +211,10 @@
                     putExtra(ControlsFavoritingActivity.EXTRA_APP,
                             listingController.getAppLabel(it))
                     putExtra(Intent.EXTRA_COMPONENT_NAME, it)
-                    putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
+                    putExtra(
+                        ControlsFavoritingActivity.EXTRA_SOURCE,
+                        ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR,
+                    )
                 }
                 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
                 animateExitAndFinish()
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 74a49a8..c954f98 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -201,8 +201,6 @@
         mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
 
         mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
-
-        mTouchInsetSession.addViewToTracking(mView);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 43e4c62..7f44463 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -101,6 +101,10 @@
 
                     completer.set(predecessor);
                 }
+
+                if (mActiveTouchSessions.isEmpty() && mStopMonitoringPending) {
+                    stopMonitoring(false);
+                }
             });
 
             return "DreamOverlayTouchMonitor::pop";
@@ -214,7 +218,12 @@
 
         @Override
         public void onPause(@NonNull LifecycleOwner owner) {
-            stopMonitoring();
+            stopMonitoring(false);
+        }
+
+        @Override
+        public void onDestroy(LifecycleOwner owner) {
+            stopMonitoring(true);
         }
     };
 
@@ -222,7 +231,7 @@
      * When invoked, instantiates a new {@link InputSession} to monitor touch events.
      */
     private void startMonitoring() {
-        stopMonitoring();
+        stopMonitoring(true);
         mCurrentInputSession = mInputSessionFactory.create(
                 "dreamOverlay",
                 mInputEventListener,
@@ -234,11 +243,16 @@
     /**
      * Destroys any active {@link InputSession}.
      */
-    private void stopMonitoring() {
+    private void stopMonitoring(boolean force) {
         if (mCurrentInputSession == null) {
             return;
         }
 
+        if (!mActiveTouchSessions.isEmpty() && !force) {
+            mStopMonitoringPending = true;
+            return;
+        }
+
         // When we stop monitoring touches, we must ensure that all active touch sessions and
         // descendants informed of the removal so any cleanup for active tracking can proceed.
         mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
@@ -250,6 +264,7 @@
 
         mCurrentInputSession.dispose();
         mCurrentInputSession = null;
+        mStopMonitoringPending = false;
     }
 
 
@@ -257,6 +272,8 @@
     private final Collection<DreamTouchHandler> mHandlers;
     private final DisplayHelper mDisplayHelper;
 
+    private boolean mStopMonitoringPending;
+
     private InputChannelCompat.InputEventListener mInputEventListener =
             new InputChannelCompat.InputEventListener() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
new file mode 100644
index 0000000..58b70b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import static com.android.systemui.dreams.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link ShadeTouchHandler} is responsible for handling swipe down gestures over dream
+ * to bring down the shade.
+ */
+public class ShadeTouchHandler implements DreamTouchHandler {
+    private final Optional<CentralSurfaces> mSurfaces;
+    private final int mInitiationHeight;
+
+    @Inject
+    ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
+            @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
+        mSurfaces = centralSurfaces;
+        mInitiationHeight = initiationHeight;
+    }
+
+    @Override
+    public void onSessionStart(TouchSession session) {
+        if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
+            session.pop();
+            return;
+        }
+
+        session.registerInputListener(ev -> {
+            final NotificationPanelViewController viewController =
+                    mSurfaces.map(CentralSurfaces::getNotificationPanelViewController).orElse(null);
+
+            if (viewController != null) {
+                viewController.handleExternalTouch((MotionEvent) ev);
+            }
+
+            if (ev instanceof MotionEvent) {
+                if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
+                    session.pop();
+                }
+            }
+        });
+
+        session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                    float distanceY) {
+                return true;
+            }
+
+            @Override
+            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                    float velocityY) {
+                return true;
+            }
+        });
+    }
+
+    @Override
+    public void getTouchInitiationRegion(Rect bounds, Region region) {
+        final Rect outBounds = new Rect(bounds);
+        outBounds.inset(0, 0, 0, outBounds.height() - mInitiationHeight);
+        region.op(outBounds, Region.Op.UNION);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
index dad0004..b719126 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
@@ -23,6 +23,7 @@
  */
 @Module(includes = {
             BouncerSwipeModule.class,
+            ShadeModule.class,
         }, subcomponents = {
             InputSessionComponent.class,
 })
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
new file mode 100644
index 0000000..9e0ae41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
@@ -0,0 +1,62 @@
+/*
+ * 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.dreams.touch.dagger;
+
+import android.content.res.Resources;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.DreamTouchHandler;
+import com.android.systemui.dreams.touch.ShadeTouchHandler;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+import javax.inject.Named;
+
+/**
+ * Dependencies for swipe down to notification over dream.
+ */
+@Module
+public class ShadeModule {
+    /**
+     * The height, defined in pixels, of the gesture initiation region at the top of the screen for
+     * swiping down notifications.
+     */
+    public static final String NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT =
+            "notification_shade_gesture_initiation_height";
+
+    /**
+     * Provides {@link ShadeTouchHandler} to handle notification swipe down over dream.
+     */
+    @Provides
+    @IntoSet
+    public static DreamTouchHandler providesNotificationShadeTouchHandler(
+            ShadeTouchHandler touchHandler) {
+        return touchHandler;
+    }
+
+    /**
+     * Provides the height of the gesture area for notification swipe down.
+     */
+    @Provides
+    @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT)
+    public static int providesNotificationShadeGestureRegionHeight(@Main Resources resources) {
+        return resources.getDimensionPixelSize(R.dimen.dream_overlay_status_bar_height);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c7b4edb..43221a7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -101,12 +101,12 @@
         releasedFlag(174148361, "notification_inline_reply_animation")
 
     val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
-        releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+        releasedFlag(254647461, "filter_unseen_notifs_on_keyguard")
 
     // TODO(b/263414400): Tracking Bug
     @JvmField
     val NOTIFICATION_ANIMATE_BIG_PICTURE =
-        releasedFlag(120, "notification_animate_big_picture", teamfood = true)
+        releasedFlag(120, "notification_animate_big_picture")
 
     @JvmField
     val ANIMATED_NOTIFICATION_SHADE_INSETS =
@@ -184,7 +184,7 @@
     // flag for controlling auto pin confirmation and material u shapes in bouncer
     @JvmField
     val AUTO_PIN_CONFIRMATION =
-        releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation", teamfood = true)
+        releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation")
 
     // TODO(b/262859270): Tracking Bug
     @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
@@ -285,7 +285,7 @@
     /** Enables Font Scaling Quick Settings tile */
     // TODO(b/269341316): Tracking Bug
     @JvmField
-    val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = false)
+    val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = true)
 
     /** Enables new QS Edit Mode visual refresh */
     // TODO(b/269787742): Tracking Bug
@@ -621,15 +621,15 @@
     @JvmField val NOTE_TASKS = releasedFlag(1900, "keycode_flag")
 
     // 2000 - device controls
-    @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels", teamfood = true)
+    @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels")
 
     @JvmField
     val APP_PANELS_ALL_APPS_ALLOWED =
-        releasedFlag(2001, "app_panels_all_apps_allowed", teamfood = true)
+        releasedFlag(2001, "app_panels_all_apps_allowed")
 
     @JvmField
     val CONTROLS_MANAGEMENT_NEW_FLOWS =
-        releasedFlag(2002, "controls_management_new_flows", teamfood = true)
+        releasedFlag(2002, "controls_management_new_flows")
 
     // Enables removing app from Home control panel as a part of a new flow
     // TODO(b/269132640): Tracking Bug
@@ -697,7 +697,7 @@
     // TODO(b/272036292): Tracking Bug
     @JvmField
     val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
-            unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true)
+            releasedFlag(2602, "large_shade_granular_alpha_interpolation")
 
     // TODO(b/272805037): Tracking Bug
     @JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
new file mode 100644
index 0000000..801b165
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -0,0 +1,493 @@
+package com.android.systemui.graphics
+
+import android.annotation.AnyThread
+import android.annotation.DrawableRes
+import android.annotation.Px
+import android.annotation.SuppressLint
+import android.annotation.WorkerThread
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.content.res.Resources.NotFoundException
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.ImageDecoder.DecodeException
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.util.Log
+import android.util.Size
+import androidx.core.content.res.ResourcesCompat
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import java.io.IOException
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+/**
+ * Helper class to load images for SystemUI. It allows for memory efficient image loading with size
+ * restriction and attempts to use hardware bitmaps when sensible.
+ */
+@SysUISingleton
+class ImageLoader
+@Inject
+constructor(
+    private val defaultContext: Context,
+    @Background private val backgroundDispatcher: CoroutineDispatcher
+) {
+
+    /** Source of the image data. */
+    sealed interface Source
+
+    /**
+     * Load image from a Resource ID. If the resource is part of another package or if it requires
+     * tinting, pass in a correct [Context].
+     */
+    data class Res(@DrawableRes val resId: Int, val context: Context?) : Source {
+        constructor(@DrawableRes resId: Int) : this(resId, null)
+    }
+
+    /** Load image from a Uri. */
+    data class Uri(val uri: android.net.Uri) : Source {
+        constructor(uri: String) : this(android.net.Uri.parse(uri))
+    }
+
+    /** Load image from a [File]. */
+    data class File(val file: java.io.File) : Source {
+        constructor(path: String) : this(java.io.File(path))
+    }
+
+    /** Load image from an [InputStream]. */
+    data class InputStream(val inputStream: java.io.InputStream, val context: Context?) : Source {
+        constructor(inputStream: java.io.InputStream) : this(inputStream, null)
+    }
+
+    /**
+     * Loads passed [Source] on a background thread and returns the [Bitmap].
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints while keeping aspect
+     * ratio.
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Bitmap] or `null` if loading failed.
+     */
+    @AnyThread
+    suspend fun loadBitmap(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Bitmap? =
+        withContext(backgroundDispatcher) { loadBitmapSync(source, maxWidth, maxHeight, allocator) }
+
+    /**
+     * Loads passed [Source] synchronously and returns the [Bitmap].
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints while keeping aspect
+     * ratio.
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Bitmap] or `null` if loading failed.
+     */
+    @WorkerThread
+    fun loadBitmapSync(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Bitmap? {
+        return try {
+            loadBitmapSync(
+                toImageDecoderSource(source, defaultContext),
+                maxWidth,
+                maxHeight,
+                allocator
+            )
+        } catch (e: NotFoundException) {
+            Log.w(TAG, "Couldn't load resource $source", e)
+            null
+        }
+    }
+
+    /**
+     * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Bitmap] or `null` if loading failed.
+     */
+    @WorkerThread
+    fun loadBitmapSync(
+        source: ImageDecoder.Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Bitmap? {
+        return try {
+            ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
+                configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+                decoder.allocator = allocator
+            }
+        } catch (e: IOException) {
+            Log.w(TAG, "Failed to load source $source", e)
+            return null
+        } catch (e: DecodeException) {
+            Log.w(TAG, "Failed to decode source $source", e)
+            return null
+        }
+    }
+
+    /**
+     * Loads passed [Source] on a background thread and returns the [Drawable].
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @AnyThread
+    suspend fun loadDrawable(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? =
+        withContext(backgroundDispatcher) {
+            loadDrawableSync(source, maxWidth, maxHeight, allocator)
+        }
+
+    /**
+     * Loads passed [Icon] on a background thread and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param context Alternate context to use for resource loading (for e.g. cross-process use)
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @AnyThread
+    suspend fun loadDrawable(
+        icon: Icon,
+        context: Context = defaultContext,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? =
+        withContext(backgroundDispatcher) {
+            loadDrawableSync(icon, context, maxWidth, maxHeight, allocator)
+        }
+
+    /**
+     * Loads passed [Source] synchronously and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @WorkerThread
+    @SuppressLint("UseCompatLoadingForDrawables")
+    fun loadDrawableSync(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? {
+        return try {
+            loadDrawableSync(
+                toImageDecoderSource(source, defaultContext),
+                maxWidth,
+                maxHeight,
+                allocator
+            )
+                ?:
+                // If we have a resource, retry fallback using the "normal" Resource loading system.
+                // This will come into effect in cases like trying to load AnimatedVectorDrawable.
+                if (source is Res) {
+                    val context = source.context ?: defaultContext
+                    ResourcesCompat.getDrawable(context.resources, source.resId, context.theme)
+                } else {
+                    null
+                }
+        } catch (e: NotFoundException) {
+            Log.w(TAG, "Couldn't load resource $source", e)
+            null
+        }
+    }
+
+    /**
+     * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @WorkerThread
+    fun loadDrawableSync(
+        source: ImageDecoder.Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? {
+        return try {
+            ImageDecoder.decodeDrawable(source) { decoder, info, _ ->
+                configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+                decoder.allocator = allocator
+            }
+        } catch (e: IOException) {
+            Log.w(TAG, "Failed to load source $source", e)
+            return null
+        } catch (e: DecodeException) {
+            Log.w(TAG, "Failed to decode source $source", e)
+            return null
+        }
+    }
+
+    /** Loads icon drawable while attempting to size restrict the drawable. */
+    @WorkerThread
+    fun loadDrawableSync(
+        icon: Icon,
+        context: Context = defaultContext,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? {
+        return when (icon.type) {
+            Icon.TYPE_URI,
+            Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+                val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+                loadDrawableSync(source, maxWidth, maxHeight, allocator)
+            }
+            Icon.TYPE_RESOURCE -> {
+                val resources = resolveResourcesForIcon(context, icon)
+                resources?.let {
+                    loadDrawableSync(
+                        ImageDecoder.createSource(it, icon.resId),
+                        maxWidth,
+                        maxHeight,
+                        allocator
+                    )
+                }
+                // Fallback to non-ImageDecoder load if the attempt failed (e.g. the resource
+                // is a Vector drawable which ImageDecoder doesn't support.)
+                ?: icon.loadDrawable(context)
+            }
+            Icon.TYPE_BITMAP -> {
+                BitmapDrawable(context.resources, icon.bitmap)
+            }
+            Icon.TYPE_ADAPTIVE_BITMAP -> {
+                AdaptiveIconDrawable(null, BitmapDrawable(context.resources, icon.bitmap))
+            }
+            Icon.TYPE_DATA -> {
+                loadDrawableSync(
+                    ImageDecoder.createSource(icon.dataBytes, icon.dataOffset, icon.dataLength),
+                    maxWidth,
+                    maxHeight,
+                    allocator
+                )
+            }
+            else -> {
+                // We don't recognize this icon, just fallback.
+                icon.loadDrawable(context)
+            }
+        }?.let { drawable ->
+            // Icons carry tint which we need to propagate down to a Drawable.
+            tintDrawable(icon, drawable)
+            drawable
+        }
+    }
+
+    companion object {
+        const val TAG = "ImageLoader"
+
+        // 4096 is a reasonable default - most devices will support 4096x4096 texture size for
+        // Canvas rendering and by default we SystemUI has no need to render larger bitmaps.
+        // This prevents exceptions and crashes if the code accidentally loads larger Bitmap
+        // and then attempts to render it on Canvas.
+        // It can always be overridden by the parameters.
+        const val DEFAULT_MAX_SAFE_BITMAP_SIZE_PX = 4096
+
+        /**
+         * This constant signals that ImageLoader shouldn't attempt to resize the passed bitmap in a
+         * given dimension.
+         *
+         * Set both maxWidth and maxHeight to [DO_NOT_RESIZE] if you wish to prevent resizing.
+         */
+        const val DO_NOT_RESIZE = 0
+
+        /** Maps [Source] to [ImageDecoder.Source]. */
+        private fun toImageDecoderSource(source: Source, defaultContext: Context) =
+            when (source) {
+                is Res -> {
+                    val context = source.context ?: defaultContext
+                    ImageDecoder.createSource(context.resources, source.resId)
+                }
+                is File -> ImageDecoder.createSource(source.file)
+                is Uri -> ImageDecoder.createSource(defaultContext.contentResolver, source.uri)
+                is InputStream -> {
+                    val context = source.context ?: defaultContext
+                    ImageDecoder.createSource(context.resources, source.inputStream)
+                }
+            }
+
+        /**
+         * This sets target size on the image decoder to conform to the maxWidth / maxHeight
+         * parameters. The parameters are chosen to keep the existing drawable aspect ratio.
+         */
+        @AnyThread
+        private fun configureDecoderForMaximumSize(
+            decoder: ImageDecoder,
+            imgSize: Size,
+            @Px maxWidth: Int,
+            @Px maxHeight: Int
+        ) {
+            if (maxWidth == DO_NOT_RESIZE && maxHeight == DO_NOT_RESIZE) {
+                return
+            }
+
+            if (imgSize.width <= maxWidth && imgSize.height <= maxHeight) {
+                return
+            }
+
+            // Determine the scale factor for each dimension so it fits within the set constraint
+            val wScale =
+                if (maxWidth <= 0) {
+                    1.0f
+                } else {
+                    maxWidth.toFloat() / imgSize.width.toFloat()
+                }
+
+            val hScale =
+                if (maxHeight <= 0) {
+                    1.0f
+                } else {
+                    maxHeight.toFloat() / imgSize.height.toFloat()
+                }
+
+            // Scale down to the dimension that demands larger scaling (smaller scale factor).
+            // Use the same scale for both dimensions to keep the aspect ratio.
+            val scale = min(wScale, hScale)
+            if (scale < 1.0f) {
+                val targetWidth = (imgSize.width * scale).toInt()
+                val targetHeight = (imgSize.height * scale).toInt()
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Configured image size to $targetWidth x $targetHeight")
+                }
+
+                decoder.setTargetSize(targetWidth, targetHeight)
+            }
+        }
+
+        /**
+         * Attempts to retrieve [Resources] class required to load the passed icon. Icons can
+         * originate from other processes so we need to make sure we load them from the right
+         * package source.
+         *
+         * @return [Resources] to load the icon drawble or null if icon doesn't carry a resource or
+         *   the resource package couldn't be resolved.
+         */
+        @WorkerThread
+        private fun resolveResourcesForIcon(context: Context, icon: Icon): Resources? {
+            if (icon.type != Icon.TYPE_RESOURCE) {
+                return null
+            }
+
+            val resources = icon.resources
+            if (resources != null) {
+                return resources
+            }
+
+            val resPackage = icon.resPackage
+            if (
+                resPackage == null || resPackage.isEmpty() || context.packageName.equals(resPackage)
+            ) {
+                return context.resources
+            }
+
+            if ("android" == resPackage) {
+                return Resources.getSystem()
+            }
+
+            val pm = context.packageManager
+            try {
+                val ai =
+                    pm.getApplicationInfo(
+                        resPackage,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES or
+                            PackageManager.GET_SHARED_LIBRARY_FILES
+                    )
+                if (ai != null) {
+                    return pm.getResourcesForApplication(ai)
+                } else {
+                    Log.w(TAG, "Failed to resolve application info for $resPackage")
+                }
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(TAG, "Failed to resolve resource package", e)
+                return null
+            }
+            return null
+        }
+
+        /** Applies tinting from [Icon] to the passed [Drawable]. */
+        @AnyThread
+        private fun tintDrawable(icon: Icon, drawable: Drawable) {
+            if (icon.hasTint()) {
+                drawable.mutate()
+                drawable.setTintList(icon.tintList)
+                drawable.setTintBlendMode(icon.tintBlendMode)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index eef7ccc..107e685 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -231,22 +231,20 @@
                 );
             }
 
-            public void mergeAnimation(IBinder transition, TransitionInfo info,
-                    SurfaceControl.Transaction t, IBinder mergeTarget,
-                    IRemoteTransitionFinishedCallback finishCallback) {
+            public void mergeAnimation(IBinder candidateTransition, TransitionInfo candidateInfo,
+                    SurfaceControl.Transaction candidateT, IBinder currentTransition,
+                    IRemoteTransitionFinishedCallback candidateFinishCallback) {
                 try {
-                    final IRemoteTransitionFinishedCallback origFinishCB;
+                    final IRemoteTransitionFinishedCallback currentFinishCB;
                     synchronized (mFinishCallbacks) {
-                        origFinishCB = mFinishCallbacks.remove(transition);
+                        currentFinishCB = mFinishCallbacks.remove(currentTransition);
                     }
-                    info.releaseAllSurfaces();
-                    t.close();
-                    if (origFinishCB == null) {
-                        // already finished (or not started yet), so do nothing.
+                    if (currentFinishCB == null) {
+                        Slog.e(TAG, "Called mergeAnimation, but finish callback is missing");
                         return;
                     }
                     runner.onAnimationCancelled(false /* isKeyguardOccluded */);
-                    origFinishCB.onTransitionFinished(null /* wct */, null /* t */);
+                    currentFinishCB.onTransitionFinished(null /* wct */, null /* t */);
                 } catch (RemoteException e) {
                     // nothing, we'll just let it finish on its own I guess.
                 }
@@ -304,13 +302,13 @@
         Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
         TransitionFilter f = new TransitionFilter();
         f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-        mShellTransitions.registerRemote(f,
-                new RemoteTransition(wrap(mExitAnimationRunner), getIApplicationThread()));
+        mShellTransitions.registerRemote(f, new RemoteTransition(
+                wrap(mExitAnimationRunner), getIApplicationThread(), "ExitKeyguard"));
 
         Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
         // Register for occluding
         final RemoteTransition occludeTransition = new RemoteTransition(
-                mOccludeAnimation, getIApplicationThread());
+                mOccludeAnimation, getIApplicationThread(), "KeyguardOcclude");
         f = new TransitionFilter();
         f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
         f.mRequirements = new TransitionFilter.Requirement[]{
@@ -329,7 +327,7 @@
 
         // Now register for un-occlude.
         final RemoteTransition unoccludeTransition = new RemoteTransition(
-                mUnoccludeAnimation, getIApplicationThread());
+                mUnoccludeAnimation, getIApplicationThread(), "KeyguardUnocclude");
         f = new TransitionFilter();
         f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
         f.mRequirements = new TransitionFilter.Requirement[]{
@@ -384,7 +382,7 @@
         f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
         mShellTransitions.registerRemote(f, new RemoteTransition(
                 wrap(mKeyguardViewMediator.getOccludeByDreamAnimationRunner()),
-                getIApplicationThread()));
+                getIApplicationThread(), "KeyguardOccludeByDream"));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0825435..b1efdd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -953,10 +953,15 @@
 
                 @Override
                 public void onAnimationCancelled(boolean isKeyguardOccluded) {
-                    if (mOccludeByDreamAnimator != null) {
-                        mOccludeByDreamAnimator.cancel();
-                    }
-                    setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
+                    mContext.getMainExecutor().execute(() -> {
+                        if (mOccludeByDreamAnimator != null) {
+                            mOccludeByDreamAnimator.cancel();
+                        }
+                    });
+                    // The value of isKeyguardOccluded here may come from mergeAnimation, which
+                    // isn't reliable. In all cases, after running or cancelling this animation,
+                    // keyguard should be occluded.
+                    setOccluded(true /* isOccluded */, false /* animate */);
                     if (DEBUG) {
                         Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: "
                                 + mOccluded);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index cb89106..6ac51cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -43,6 +43,7 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
+import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule;
 import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
@@ -66,8 +67,6 @@
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.CoroutineScope;
 
 /**
  * Dagger Module providing keyguard.
@@ -82,6 +81,7 @@
             KeyguardDataQuickAffordanceModule.class,
             KeyguardQuickAffordanceModule.class,
             KeyguardRepositoryModule.class,
+            KeyguardFaceAuthModule.class,
             StartKeyguardTransitionModule.class,
         })
 public class KeyguardModule {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index d5129a6..09002fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -87,6 +87,13 @@
      */
     val isStrongBiometricAllowed: StateFlow<Boolean>
 
+    /**
+     * Whether the current user is allowed to use a convenience biometric for device entry based on
+     * Android Security policies. If false, the user may be able to use strong biometric or primary
+     * authentication for device entry.
+     */
+    val isNonStrongBiometricAllowed: StateFlow<Boolean>
+
     /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */
     val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean>
 
@@ -276,6 +283,16 @@
             )
         )
 
+    override val isNonStrongBiometricAllowed: StateFlow<Boolean> =
+        strongAuthTracker.isNonStrongBiometricAllowed.stateIn(
+            scope,
+            SharingStarted.Eagerly,
+            strongAuthTracker.isBiometricAllowedForUser(
+                false,
+                userRepository.getSelectedUserInfo().id
+            )
+        )
+
     override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
         selectedUserId
             .flatMapLatest { userId ->
@@ -297,40 +314,62 @@
 private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
     LockPatternUtils.StrongAuthTracker(context) {
 
-    private val _authFlags =
+    // Backing field for onStrongAuthRequiredChanged
+    private val _strongAuthFlags =
         MutableStateFlow(
             StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
         )
 
+    // Backing field for onIsNonStrongBiometricAllowedChanged
+    private val _nonStrongBiometricAllowed =
+        MutableStateFlow(
+            Pair(currentUserId, isNonStrongBiometricAllowedAfterIdleTimeout(currentUserId))
+        )
+
     val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
         userRepository.selectedUserInfo
             .map { it.id }
             .distinctUntilChanged()
-            .flatMapLatest { currUserId ->
-                _authFlags
-                    .filter { it.userId == currUserId }
+            .flatMapLatest { userId ->
+                _strongAuthFlags
+                    .filter { it.userId == userId }
                     .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
                     .onStart {
-                        emit(
-                            StrongAuthenticationFlags(
-                                currentUserId,
-                                getStrongAuthForUser(currentUserId)
-                            )
-                        )
+                        emit(StrongAuthenticationFlags(userId, getStrongAuthForUser(userId)))
                     }
             }
 
+    /** isStrongBiometricAllowed for the current user. */
     val isStrongBiometricAllowed: Flow<Boolean> =
         currentUserAuthFlags.map { isBiometricAllowedForUser(true, it.userId) }
 
+    /** isNonStrongBiometricAllowed for the current user. */
+    val isNonStrongBiometricAllowed: Flow<Boolean> =
+        userRepository.selectedUserInfo
+            .map { it.id }
+            .distinctUntilChanged()
+            .flatMapLatest { userId ->
+                _nonStrongBiometricAllowed
+                    .filter { it.first == userId }
+                    .map { it.second }
+                    .onEach { Log.d(TAG, "isNonStrongBiometricAllowed changed for current user") }
+                    .onStart { emit(isNonStrongBiometricAllowedAfterIdleTimeout(userId)) }
+            }
+
     private val currentUserId
         get() = userRepository.getSelectedUserInfo().id
 
     override fun onStrongAuthRequiredChanged(userId: Int) {
         val newFlags = getStrongAuthForUser(userId)
-        _authFlags.value = StrongAuthenticationFlags(userId, newFlags)
+        _strongAuthFlags.value = StrongAuthenticationFlags(userId, newFlags)
         Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
     }
+
+    override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
+        val allowed = isNonStrongBiometricAllowedAfterIdleTimeout(userId)
+        _nonStrongBiometricAllowed.value = Pair(userId, allowed)
+        Log.d(TAG, "onIsNonStrongBiometricAllowedChanged for userId: $userId, $allowed")
+    }
 }
 
 private fun DevicePolicyManager.isFaceDisabled(userId: Int): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
new file mode 100644
index 0000000..56e7398
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceManager
+import android.os.CancellationSignal
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.UserRepository
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * API to run face authentication and detection for device entry / on keyguard (as opposed to the
+ * biometric prompt).
+ */
+interface DeviceEntryFaceAuthRepository {
+    /** Provide the current face authentication state for device entry. */
+    val isAuthenticated: Flow<Boolean>
+
+    /** Whether face auth can run at this point. */
+    val canRunFaceAuth: Flow<Boolean>
+
+    /** Provide the current status of face authentication. */
+    val authenticationStatus: Flow<AuthenticationStatus>
+
+    /** Provide the current status of face detection. */
+    val detectionStatus: Flow<DetectionStatus>
+
+    /** Current state of whether face authentication is locked out or not. */
+    val isLockedOut: Flow<Boolean>
+
+    /** Current state of whether face authentication is running. */
+    val isAuthRunning: Flow<Boolean>
+
+    /**
+     * Trigger face authentication.
+     *
+     * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
+     * ignored if face authentication is already running. Results should be propagated through
+     * [authenticationStatus]
+     *
+     * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+     */
+    suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+
+    /** Stop currently running face authentication or detection. */
+    fun cancel()
+}
+
+@SysUISingleton
+class DeviceEntryFaceAuthRepositoryImpl
+@Inject
+constructor(
+    context: Context,
+    private val faceManager: FaceManager? = null,
+    private val userRepository: UserRepository,
+    private val keyguardBypassController: KeyguardBypassController? = null,
+    @Application private val applicationScope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    private val sessionTracker: SessionTracker,
+    private val uiEventsLogger: UiEventLogger,
+    private val faceAuthLogger: FaceAuthenticationLogger,
+    private val biometricSettingsRepository: BiometricSettingsRepository,
+    private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+    private val trustRepository: TrustRepository,
+    private val keyguardRepository: KeyguardRepository,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    dumpManager: DumpManager,
+) : DeviceEntryFaceAuthRepository, Dumpable {
+    private var authCancellationSignal: CancellationSignal? = null
+    private var detectCancellationSignal: CancellationSignal? = null
+    private var faceAcquiredInfoIgnoreList: Set<Int>
+
+    private var cancelNotReceivedHandlerJob: Job? = null
+
+    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+        MutableStateFlow(null)
+    override val authenticationStatus: Flow<AuthenticationStatus>
+        get() = _authenticationStatus.filterNotNull()
+
+    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
+    override val detectionStatus: Flow<DetectionStatus>
+        get() = _detectionStatus.filterNotNull()
+
+    private val _isLockedOut = MutableStateFlow(false)
+    override val isLockedOut: StateFlow<Boolean> = _isLockedOut
+
+    val isDetectionSupported =
+        faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
+
+    private val _isAuthRunning = MutableStateFlow(false)
+    override val isAuthRunning: StateFlow<Boolean>
+        get() = _isAuthRunning
+
+    private val keyguardSessionId: InstanceId?
+        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
+
+    private val _canRunFaceAuth = MutableStateFlow(true)
+    override val canRunFaceAuth: StateFlow<Boolean>
+        get() = _canRunFaceAuth
+
+    private val canRunDetection = MutableStateFlow(false)
+
+    private val _isAuthenticated = MutableStateFlow(false)
+    override val isAuthenticated: Flow<Boolean>
+        get() = _isAuthenticated
+
+    private val bypassEnabled: Flow<Boolean> =
+        keyguardBypassController?.let {
+            conflatedCallbackFlow {
+                val callback =
+                    object : KeyguardBypassController.OnBypassStateChangedListener {
+                        override fun onBypassStateChanged(isEnabled: Boolean) {
+                            trySendWithFailureLogging(isEnabled, TAG, "BypassStateChanged")
+                        }
+                    }
+                it.registerOnBypassStateChangedListener(callback)
+                trySendWithFailureLogging(it.bypassEnabled, TAG, "BypassStateChanged")
+                awaitClose { it.unregisterOnBypassStateChangedListener(callback) }
+            }
+        }
+            ?: flowOf(false)
+
+    private val faceLockoutResetCallback =
+        object : FaceManager.LockoutResetCallback() {
+            override fun onLockoutReset(sensorId: Int) {
+                _isLockedOut.value = false
+            }
+        }
+
+    init {
+        faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
+        faceAcquiredInfoIgnoreList =
+            Arrays.stream(
+                    context.resources.getIntArray(
+                        R.array.config_face_acquire_device_entry_ignorelist
+                    )
+                )
+                .boxed()
+                .collect(Collectors.toSet())
+        dumpManager.registerCriticalDumpable("DeviceEntryFaceAuthRepositoryImpl", this)
+
+        observeFaceAuthGatingChecks()
+        observeFaceDetectGatingChecks()
+        observeFaceAuthResettingConditions()
+    }
+
+    private fun observeFaceAuthResettingConditions() {
+        // Clear auth status when keyguard is going away or when the user is switching.
+        merge(keyguardRepository.isKeyguardGoingAway, userRepository.userSwitchingInProgress)
+            .onEach { goingAwayOrUserSwitchingInProgress ->
+                if (goingAwayOrUserSwitchingInProgress) {
+                    _isAuthenticated.value = false
+                }
+            }
+            .launchIn(applicationScope)
+    }
+
+    private fun observeFaceDetectGatingChecks() {
+        // Face detection can run only when lockscreen bypass is enabled
+        // & detection is supported & biometric unlock is not allowed.
+        listOf(
+                canFaceAuthOrDetectRun(),
+                logAndObserve(bypassEnabled, "bypassEnabled"),
+                logAndObserve(
+                    biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(),
+                    "nonStrongBiometricIsNotAllowed"
+                ),
+                // We don't want to run face detect if it's not possible to authenticate with FP
+                // from the bouncer. UDFPS is the only fp sensor type that won't support this.
+                logAndObserve(
+                    and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
+                    "udfpsAuthIsNotPossibleAnymore"
+                )
+            )
+            .reduce(::and)
+            .distinctUntilChanged()
+            .onEach {
+                faceAuthLogger.canRunDetectionChanged(it)
+                canRunDetection.value = it
+                if (!it) {
+                    cancelDetection()
+                }
+            }
+            .launchIn(applicationScope)
+    }
+
+    private fun isUdfps() =
+        deviceEntryFingerprintAuthRepository.availableFpSensorType.map {
+            it == BiometricType.UNDER_DISPLAY_FINGERPRINT
+        }
+
+    private fun canFaceAuthOrDetectRun(): Flow<Boolean> {
+        return listOf(
+                logAndObserve(biometricSettingsRepository.isFaceEnrolled, "isFaceEnrolled"),
+                logAndObserve(
+                    biometricSettingsRepository.isFaceAuthenticationEnabled,
+                    "isFaceAuthenticationEnabled"
+                ),
+                logAndObserve(
+                    userRepository.userSwitchingInProgress.isFalse(),
+                    "userSwitchingNotInProgress"
+                ),
+                logAndObserve(
+                    keyguardRepository.isKeyguardGoingAway.isFalse(),
+                    "keyguardNotGoingAway"
+                ),
+                logAndObserve(
+                    keyguardRepository.wakefulness
+                        .map { WakefulnessModel.isSleepingOrStartingToSleep(it) }
+                        .isFalse(),
+                    "deviceNotSleepingOrNotStartingToSleep"
+                ),
+                logAndObserve(
+                    combine(
+                        keyguardInteractor.isSecureCameraActive,
+                        alternateBouncerInteractor.isVisible,
+                    ) { a, b ->
+                        !a || b
+                    },
+                    "secureCameraNotActiveOrAltBouncerIsShowing"
+                ),
+                logAndObserve(
+                    biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture,
+                    "isFaceAuthSupportedInCurrentPosture"
+                ),
+                logAndObserve(
+                    biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
+                    "userHasNotLockedDownDevice"
+                )
+            )
+            .reduce(::and)
+    }
+
+    private fun observeFaceAuthGatingChecks() {
+        // Face auth can run only if all of the gating conditions are true.
+        listOf(
+                canFaceAuthOrDetectRun(),
+                logAndObserve(isLockedOut.isFalse(), "isNotLocked"),
+                logAndObserve(
+                    deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
+                    "fpLockedOut"
+                ),
+                logAndObserve(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserTrusted"),
+                logAndObserve(
+                    biometricSettingsRepository.isNonStrongBiometricAllowed,
+                    "nonStrongBiometricIsAllowed"
+                ),
+                logAndObserve(
+                    userRepository.selectedUserInfo.map { it.isPrimary },
+                    "userIsPrimaryUser"
+                ),
+            )
+            .reduce(::and)
+            .distinctUntilChanged()
+            .onEach {
+                faceAuthLogger.canFaceAuthRunChanged(it)
+                _canRunFaceAuth.value = it
+                if (!it) {
+                    // Cancel currently running auth if any of the gating checks are false.
+                    faceAuthLogger.cancellingFaceAuth()
+                    cancel()
+                }
+            }
+            .launchIn(applicationScope)
+    }
+
+    private val faceAuthCallback =
+        object : FaceManager.AuthenticationCallback() {
+            override fun onAuthenticationFailed() {
+                _authenticationStatus.value = FailedAuthenticationStatus
+                _isAuthenticated.value = false
+                faceAuthLogger.authenticationFailed()
+                onFaceAuthRequestCompleted()
+            }
+
+            override fun onAuthenticationAcquired(acquireInfo: Int) {
+                _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+                faceAuthLogger.authenticationAcquired(acquireInfo)
+            }
+
+            override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
+                val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+                if (errorStatus.isLockoutError()) {
+                    _isLockedOut.value = true
+                }
+                _authenticationStatus.value = errorStatus
+                _isAuthenticated.value = false
+                if (errorStatus.isCancellationError()) {
+                    cancelNotReceivedHandlerJob?.cancel()
+                    applicationScope.launch {
+                        faceAuthLogger.launchingQueuedFaceAuthRequest(
+                            faceAuthRequestedWhileCancellation
+                        )
+                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
+                        faceAuthRequestedWhileCancellation = null
+                    }
+                }
+                faceAuthLogger.authenticationError(
+                    errorCode,
+                    errString,
+                    errorStatus.isLockoutError(),
+                    errorStatus.isCancellationError()
+                )
+                onFaceAuthRequestCompleted()
+            }
+
+            override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
+                if (faceAcquiredInfoIgnoreList.contains(code)) {
+                    return
+                }
+                _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+            }
+
+            override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
+                _authenticationStatus.value = SuccessAuthenticationStatus(result)
+                _isAuthenticated.value = true
+                faceAuthLogger.faceAuthSuccess(result)
+                onFaceAuthRequestCompleted()
+            }
+        }
+
+    private fun onFaceAuthRequestCompleted() {
+        cancellationInProgress = false
+        _isAuthRunning.value = false
+        authCancellationSignal = null
+    }
+
+    private val detectionCallback =
+        FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
+            faceAuthLogger.faceDetected()
+            _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+        }
+
+    private var cancellationInProgress = false
+    private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+
+    override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+        if (_isAuthRunning.value) {
+            faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
+            return
+        }
+
+        if (cancellationInProgress) {
+            faceAuthLogger.queuingRequestWhileCancelling(
+                faceAuthRequestedWhileCancellation,
+                uiEvent
+            )
+            faceAuthRequestedWhileCancellation = uiEvent
+            return
+        } else {
+            faceAuthRequestedWhileCancellation = null
+        }
+
+        if (canRunFaceAuth.value) {
+            withContext(mainDispatcher) {
+                // We always want to invoke face auth in the main thread.
+                authCancellationSignal = CancellationSignal()
+                _isAuthRunning.value = true
+                uiEventsLogger.logWithInstanceIdAndPosition(
+                    uiEvent,
+                    0,
+                    null,
+                    keyguardSessionId,
+                    uiEvent.extraInfo
+                )
+                faceAuthLogger.authenticating(uiEvent)
+                faceManager?.authenticate(
+                    null,
+                    authCancellationSignal,
+                    faceAuthCallback,
+                    null,
+                    FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+                )
+            }
+        } else if (fallbackToDetection && canRunDetection.value) {
+            detect()
+        }
+    }
+
+    suspend fun detect() {
+        if (!isDetectionSupported) {
+            faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
+            return
+        }
+        if (_isAuthRunning.value || detectCancellationSignal != null) {
+            faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
+            return
+        }
+
+        detectCancellationSignal = CancellationSignal()
+        withContext(mainDispatcher) {
+            // We always want to invoke face detect in the main thread.
+            faceAuthLogger.faceDetectionStarted()
+            faceManager?.detectFace(
+                detectCancellationSignal,
+                detectionCallback,
+                FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+            )
+        }
+    }
+
+    private val currentUserId: Int
+        get() = userRepository.getSelectedUserInfo().id
+
+    fun cancelDetection() {
+        detectCancellationSignal?.cancel()
+        detectCancellationSignal = null
+    }
+
+    override fun cancel() {
+        if (authCancellationSignal == null) return
+
+        authCancellationSignal?.cancel()
+        cancelNotReceivedHandlerJob =
+            applicationScope.launch {
+                delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+                faceAuthLogger.cancelSignalNotReceived(
+                    _isAuthRunning.value,
+                    _isLockedOut.value,
+                    cancellationInProgress,
+                    faceAuthRequestedWhileCancellation
+                )
+                onFaceAuthRequestCompleted()
+            }
+        cancellationInProgress = true
+        _isAuthRunning.value = false
+    }
+
+    private fun logAndObserve(cond: Flow<Boolean>, loggingContext: String): Flow<Boolean> {
+        return cond.distinctUntilChanged().onEach {
+            faceAuthLogger.observedConditionChanged(it, loggingContext)
+        }
+    }
+
+    companion object {
+        const val TAG = "DeviceEntryFaceAuthRepository"
+
+        /**
+         * If no cancel signal has been received after this amount of time, assume that it is
+         * cancelled.
+         */
+        const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("DeviceEntryFaceAuthRepositoryImpl state:")
+        pw.println("  cancellationInProgress: $cancellationInProgress")
+        pw.println("  _isLockedOut.value: ${_isLockedOut.value}")
+        pw.println("  _isAuthRunning.value: ${_isAuthRunning.value}")
+        pw.println("  isDetectionSupported: $isDetectionSupported")
+        pw.println("  FaceManager state:")
+        pw.println("    faceManager: $faceManager")
+        pw.println("    sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
+        pw.println(
+            "    supportsFaceDetection: " +
+                "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
+        )
+        pw.println(
+            "  faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
+        )
+        pw.println("  authCancellationSignal: $authCancellationSignal")
+        pw.println("  detectCancellationSignal: $detectCancellationSignal")
+        pw.println("  faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
+        pw.println("  _authenticationStatus: ${_authenticationStatus.value}")
+        pw.println("  _detectionStatus: ${_detectionStatus.value}")
+        pw.println("  currentUserId: $currentUserId")
+        pw.println("  keyguardSessionId: $keyguardSessionId")
+        pw.println("  lockscreenBypassEnabled: ${keyguardBypassController?.bypassEnabled ?: false}")
+    }
+}
+/** Combine two boolean flows by and-ing both of them */
+private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
+    flow.combine(anotherFlow) { a, b -> a && b }
+
+/** "Not" the given flow. The return [Flow] will be true when [this] flow is false. */
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> {
+    return this.map { !it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 4fa56ee..52234b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.hardware.biometrics.BiometricAuthenticator
+import android.hardware.biometrics.BiometricAuthenticator.Modality
 import android.hardware.biometrics.BiometricSourceType
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -33,6 +35,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -49,7 +52,7 @@
     /**
      * Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
      */
-    val availableFpSensorType: BiometricType?
+    val availableFpSensorType: Flow<BiometricType?>
 }
 
 /**
@@ -77,11 +80,39 @@
         pw.println("isLockedOut=${isLockedOut.value}")
     }
 
-    override val availableFpSensorType: BiometricType?
-        get() =
-            if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
-            else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
-            else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
+    override val availableFpSensorType: Flow<BiometricType?>
+        get() {
+            return if (authController.areAllFingerprintAuthenticatorsRegistered()) {
+                flowOf(getFpSensorType())
+            } else {
+                conflatedCallbackFlow {
+                    val callback =
+                        object : AuthController.Callback {
+                            override fun onAllAuthenticatorsRegistered(@Modality modality: Int) {
+                                if (modality == BiometricAuthenticator.TYPE_FINGERPRINT)
+                                    trySendWithFailureLogging(
+                                        getFpSensorType(),
+                                        TAG,
+                                        "onAllAuthenticatorsRegistered, emitting fpSensorType"
+                                    )
+                            }
+                        }
+                    authController.addCallback(callback)
+                    trySendWithFailureLogging(
+                        getFpSensorType(),
+                        TAG,
+                        "initial value for fpSensorType"
+                    )
+                    awaitClose { authController.removeCallback(callback) }
+                }
+            }
+        }
+
+    private fun getFpSensorType(): BiometricType? {
+        return if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
+        else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
+        else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
+    }
 
     override val isLockedOut: StateFlow<Boolean> =
         conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 64e2a2c..0b506cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -65,8 +65,6 @@
     val keyguardAuthenticated: StateFlow<Boolean?>
     val showMessage: StateFlow<BouncerShowMessageModel?>
     val resourceUpdateRequests: StateFlow<Boolean>
-    val bouncerPromptReason: Int
-    val bouncerErrorMessage: CharSequence?
     val alternateBouncerVisible: StateFlow<Boolean>
     val alternateBouncerUIAvailable: StateFlow<Boolean>
     val sideFpsShowing: StateFlow<Boolean>
@@ -145,11 +143,6 @@
     override val showMessage = _showMessage.asStateFlow()
     private val _resourceUpdateRequests = MutableStateFlow(false)
     override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
-    override val bouncerPromptReason: Int
-        get() = viewMediatorCallback.bouncerPromptReason
-    override val bouncerErrorMessage: CharSequence?
-        get() = viewMediatorCallback.consumeCustomMessage()
-
     /** Values associated with the AlternateBouncer */
     private val _alternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
deleted file mode 100644
index a326840..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.app.StatusBarManager
-import android.content.Context
-import android.hardware.face.FaceAuthenticateOptions
-import android.hardware.face.FaceManager
-import android.os.CancellationSignal
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.systemui.Dumpable
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
-import com.android.systemui.log.FaceAuthenticationLogger
-import com.android.systemui.log.SessionTracker
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.user.data.repository.UserRepository
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-/**
- * API to run face authentication and detection for device entry / on keyguard (as opposed to the
- * biometric prompt).
- */
-interface KeyguardFaceAuthManager {
-    /**
-     * Trigger face authentication.
-     *
-     * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
-     * ignored if face authentication is already running. Results should be propagated through
-     * [authenticationStatus]
-     */
-    suspend fun authenticate(uiEvent: FaceAuthUiEvent)
-
-    /**
-     * Trigger face detection.
-     *
-     * Invocation should be ignored if face authentication is currently running.
-     */
-    suspend fun detect()
-
-    /** Stop currently running face authentication or detection. */
-    fun cancel()
-
-    /** Provide the current status of face authentication. */
-    val authenticationStatus: Flow<AuthenticationStatus>
-
-    /** Provide the current status of face detection. */
-    val detectionStatus: Flow<DetectionStatus>
-
-    /** Current state of whether face authentication is locked out or not. */
-    val isLockedOut: Flow<Boolean>
-
-    /** Current state of whether face authentication is running. */
-    val isAuthRunning: Flow<Boolean>
-
-    /** Is face detection supported. */
-    val isDetectionSupported: Boolean
-}
-
-@SysUISingleton
-class KeyguardFaceAuthManagerImpl
-@Inject
-constructor(
-    context: Context,
-    private val faceManager: FaceManager? = null,
-    private val userRepository: UserRepository,
-    private val keyguardBypassController: KeyguardBypassController? = null,
-    @Application private val applicationScope: CoroutineScope,
-    @Main private val mainDispatcher: CoroutineDispatcher,
-    private val sessionTracker: SessionTracker,
-    private val uiEventsLogger: UiEventLogger,
-    private val faceAuthLogger: FaceAuthenticationLogger,
-    dumpManager: DumpManager,
-) : KeyguardFaceAuthManager, Dumpable {
-    private var cancellationSignal: CancellationSignal? = null
-    private val lockscreenBypassEnabled: Boolean
-        get() = keyguardBypassController?.bypassEnabled ?: false
-    private var faceAcquiredInfoIgnoreList: Set<Int>
-
-    private val faceLockoutResetCallback =
-        object : FaceManager.LockoutResetCallback() {
-            override fun onLockoutReset(sensorId: Int) {
-                _isLockedOut.value = false
-            }
-        }
-
-    init {
-        faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
-        faceAcquiredInfoIgnoreList =
-            Arrays.stream(
-                    context.resources.getIntArray(
-                        R.array.config_face_acquire_device_entry_ignorelist
-                    )
-                )
-                .boxed()
-                .collect(Collectors.toSet())
-        dumpManager.registerCriticalDumpable("KeyguardFaceAuthManagerImpl", this)
-    }
-
-    private val faceAuthCallback =
-        object : FaceManager.AuthenticationCallback() {
-            override fun onAuthenticationFailed() {
-                _authenticationStatus.value = FailedAuthenticationStatus
-                faceAuthLogger.authenticationFailed()
-                onFaceAuthRequestCompleted()
-            }
-
-            override fun onAuthenticationAcquired(acquireInfo: Int) {
-                _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
-                faceAuthLogger.authenticationAcquired(acquireInfo)
-            }
-
-            override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
-                val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
-                if (errorStatus.isLockoutError()) {
-                    _isLockedOut.value = true
-                }
-                _authenticationStatus.value = errorStatus
-                if (errorStatus.isCancellationError()) {
-                    cancelNotReceivedHandlerJob?.cancel()
-                    applicationScope.launch {
-                        faceAuthLogger.launchingQueuedFaceAuthRequest(
-                            faceAuthRequestedWhileCancellation
-                        )
-                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
-                        faceAuthRequestedWhileCancellation = null
-                    }
-                }
-                faceAuthLogger.authenticationError(
-                    errorCode,
-                    errString,
-                    errorStatus.isLockoutError(),
-                    errorStatus.isCancellationError()
-                )
-                onFaceAuthRequestCompleted()
-            }
-
-            override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
-                if (faceAcquiredInfoIgnoreList.contains(code)) {
-                    return
-                }
-                _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
-            }
-
-            override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
-                _authenticationStatus.value = SuccessAuthenticationStatus(result)
-                faceAuthLogger.faceAuthSuccess(result)
-                onFaceAuthRequestCompleted()
-            }
-        }
-
-    private fun onFaceAuthRequestCompleted() {
-        cancellationInProgress = false
-        _isAuthRunning.value = false
-        cancellationSignal = null
-    }
-
-    private val detectionCallback =
-        FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
-            faceAuthLogger.faceDetected()
-            _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
-        }
-
-    private var cancellationInProgress = false
-    private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
-
-    override suspend fun authenticate(uiEvent: FaceAuthUiEvent) {
-        if (_isAuthRunning.value) {
-            faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
-            return
-        }
-
-        if (cancellationInProgress) {
-            faceAuthLogger.queuingRequestWhileCancelling(
-                faceAuthRequestedWhileCancellation,
-                uiEvent
-            )
-            faceAuthRequestedWhileCancellation = uiEvent
-            return
-        } else {
-            faceAuthRequestedWhileCancellation = null
-        }
-
-        withContext(mainDispatcher) {
-            // We always want to invoke face auth in the main thread.
-            cancellationSignal = CancellationSignal()
-            _isAuthRunning.value = true
-            uiEventsLogger.logWithInstanceIdAndPosition(
-                uiEvent,
-                0,
-                null,
-                keyguardSessionId,
-                uiEvent.extraInfo
-            )
-            faceAuthLogger.authenticating(uiEvent)
-            faceManager?.authenticate(
-                null,
-                cancellationSignal,
-                faceAuthCallback,
-                null,
-                FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
-            )
-        }
-    }
-
-    override suspend fun detect() {
-        if (!isDetectionSupported) {
-            faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
-            return
-        }
-        if (_isAuthRunning.value) {
-            faceAuthLogger.skippingBecauseAlreadyRunning("detection")
-            return
-        }
-
-        cancellationSignal = CancellationSignal()
-        withContext(mainDispatcher) {
-            // We always want to invoke face detect in the main thread.
-            faceAuthLogger.faceDetectionStarted()
-            faceManager?.detectFace(
-                cancellationSignal,
-                detectionCallback,
-                FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
-            )
-        }
-    }
-
-    private val currentUserId: Int
-        get() = userRepository.getSelectedUserInfo().id
-
-    override fun cancel() {
-        if (cancellationSignal == null) return
-
-        cancellationSignal?.cancel()
-        cancelNotReceivedHandlerJob =
-            applicationScope.launch {
-                delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
-                faceAuthLogger.cancelSignalNotReceived(
-                    _isAuthRunning.value,
-                    _isLockedOut.value,
-                    cancellationInProgress,
-                    faceAuthRequestedWhileCancellation
-                )
-                onFaceAuthRequestCompleted()
-            }
-        cancellationInProgress = true
-        _isAuthRunning.value = false
-    }
-
-    private var cancelNotReceivedHandlerJob: Job? = null
-
-    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
-        MutableStateFlow(null)
-    override val authenticationStatus: Flow<AuthenticationStatus>
-        get() = _authenticationStatus.filterNotNull()
-
-    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
-    override val detectionStatus: Flow<DetectionStatus>
-        get() = _detectionStatus.filterNotNull()
-
-    private val _isLockedOut = MutableStateFlow(false)
-    override val isLockedOut: Flow<Boolean> = _isLockedOut
-
-    override val isDetectionSupported =
-        faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
-
-    private val _isAuthRunning = MutableStateFlow(false)
-    override val isAuthRunning: Flow<Boolean>
-        get() = _isAuthRunning
-
-    private val keyguardSessionId: InstanceId?
-        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
-
-    companion object {
-        const val TAG = "KeyguardFaceAuthManager"
-
-        /**
-         * If no cancel signal has been received after this amount of time, assume that it is
-         * cancelled.
-         */
-        const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("KeyguardFaceAuthManagerImpl state:")
-        pw.println("  cancellationInProgress: $cancellationInProgress")
-        pw.println("  _isLockedOut.value: ${_isLockedOut.value}")
-        pw.println("  _isAuthRunning.value: ${_isAuthRunning.value}")
-        pw.println("  isDetectionSupported: $isDetectionSupported")
-        pw.println("  FaceManager state:")
-        pw.println("    faceManager: $faceManager")
-        pw.println("    sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
-        pw.println(
-            "    supportsFaceDetection: " +
-                "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
-        )
-        pw.println(
-            "  faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
-        )
-        pw.println("  cancellationSignal: $cancellationSignal")
-        pw.println("  faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
-        pw.println("  _authenticationStatus: ${_authenticationStatus.value}")
-        pw.println("  _detectionStatus: ${_detectionStatus.value}")
-        pw.println("  currentUserId: $currentUserId")
-        pw.println("  keyguardSessionId: $keyguardSessionId")
-        pw.println("  lockscreenBypassEnabled: $lockscreenBypassEnabled")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
new file mode 100644
index 0000000..3c66f24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface KeyguardFaceAuthModule {
+    @Binds
+    fun deviceEntryFaceAuthRepository(
+        impl: DeviceEntryFaceAuthRepositoryImpl
+    ): DeviceEntryFaceAuthRepository
+
+    @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 33f4e2e..9212aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -27,7 +27,6 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.settingslib.Utils
 import com.android.systemui.DejankUtils
 import com.android.systemui.R
 import com.android.systemui.classifier.FalsingCollector
@@ -42,12 +41,12 @@
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
+import javax.inject.Inject
 
 /**
  * Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password)
@@ -82,12 +81,6 @@
     /** Runnable to show the primary bouncer. */
     val showRunnable = Runnable {
         repository.setPrimaryShow(true)
-        primaryBouncerView.delegate?.showPromptReason(repository.bouncerPromptReason)
-        (repository.bouncerErrorMessage as? String)?.let {
-            repository.setShowMessage(
-                BouncerShowMessageModel(message = it, Utils.getColorError(context))
-            )
-        }
         repository.setPrimaryShowingSoon(false)
         primaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.VISIBLE)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index b1c5f8f..eded9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -18,7 +18,10 @@
 
 import android.hardware.face.FaceManager
 
-/** Authentication status provided by [com.android.keyguard.faceauth.KeyguardFaceAuthManager] */
+/**
+ * Authentication status provided by
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
+ */
 sealed class AuthenticationStatus
 
 /** Success authentication status. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 468a6b5..927df55 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -121,6 +121,7 @@
                                     securityContainerController.showPrimarySecurityScreen(
                                         /* turningOff= */ false
                                     )
+                                    securityContainerController.setInitialMessage()
                                     securityContainerController.appear()
                                     securityContainerController.onResume(
                                         KeyguardSecurityView.SCREEN_ON
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 647e3a1..f7355d5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -7,17 +7,17 @@
 import com.android.systemui.log.dagger.FaceAuthLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel.DEBUG
-import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
-private const val TAG = "KeyguardFaceAuthManagerLog"
+private const val TAG = "DeviceEntryFaceAuthRepositoryLog"
 
 /**
- * Helper class for logging for [com.android.keyguard.faceauth.KeyguardFaceAuthManager]
+ * Helper class for logging for
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
  *
  * To enable logcat echoing for an entire buffer:
  * ```
- *   adb shell settings put global systemui/buffer/KeyguardFaceAuthManagerLog <logLevel>
+ *   adb shell settings put global systemui/buffer/DeviceEntryFaceAuthRepositoryLog <logLevel>
  *
  * ```
  */
@@ -82,8 +82,19 @@
         )
     }
 
-    fun skippingBecauseAlreadyRunning(@CompileTimeConstant operation: String) {
-        logBuffer.log(TAG, DEBUG, "isAuthRunning is true, skipping $operation")
+    fun skippingDetection(isAuthRunning: Boolean, detectCancellationNotNull: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = isAuthRunning
+                bool2 = detectCancellationNotNull
+            },
+            {
+                "Skipping running detection: isAuthRunning: $bool1, " +
+                    "detectCancellationNotNull: $bool2"
+            }
+        )
     }
 
     fun faceDetectionStarted() {
@@ -177,4 +188,33 @@
             { "Face authenticated successfully: userId: $int1, isStrongBiometric: $bool1" }
         )
     }
+
+    fun observedConditionChanged(newValue: Boolean, context: String) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = newValue
+                str1 = context
+            },
+            { "Observed condition changed: $str1, new value: $bool1" }
+        )
+    }
+
+    fun canFaceAuthRunChanged(canRun: Boolean) {
+        logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
+    }
+
+    fun canRunDetectionChanged(canRunDetection: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = canRunDetection },
+            { "canRunDetection value changed to $bool1" }
+        )
+    }
+
+    fun cancellingFaceAuth() {
+        logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
index b98a92f..d848b43 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
@@ -28,6 +28,8 @@
 
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -60,6 +62,7 @@
     private final AuthController mAuthController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardStateController mKeyguardStateController;
+    private final UiEventLogger mUiEventLogger;
     private final Map<Integer, InstanceId> mSessionToInstanceId = new HashMap<>();
 
     private boolean mKeyguardSessionStarted;
@@ -69,12 +72,14 @@
             IStatusBarService statusBarService,
             AuthController authController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            KeyguardStateController keyguardStateController
+            KeyguardStateController keyguardStateController,
+            UiEventLogger uiEventLogger
     ) {
         mStatusBarManagerService = statusBarService;
         mAuthController = authController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardStateController = keyguardStateController;
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -116,6 +121,10 @@
     }
 
     private void endSession(int type) {
+        endSession(type, null);
+    }
+
+    private void endSession(int type, @Nullable SessionUiEvent endSessionUiEvent) {
         if (mSessionToInstanceId.getOrDefault(type, null) == null) {
             Log.e(TAG, "session [" + getString(type) + "] was not started");
             return;
@@ -127,6 +136,9 @@
             if (DEBUG) {
                 Log.d(TAG, "Session end for [" + getString(type) + "] id=" + instanceId);
             }
+            if (endSessionUiEvent != null) {
+                mUiEventLogger.log(endSessionUiEvent, instanceId);
+            }
             mStatusBarManagerService.onSessionEnded(type, instanceId);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to send onSessionEnded for session="
@@ -139,7 +151,7 @@
         @Override
         public void onStartedGoingToSleep(int why) {
             if (mKeyguardSessionStarted) {
-                endSession(SESSION_KEYGUARD);
+                endSession(SESSION_KEYGUARD, SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP);
             }
 
             // Start a new session whenever the device goes to sleep
@@ -162,7 +174,8 @@
                 startSession(SESSION_KEYGUARD);
             } else if (!keyguardShowing && wasSessionStarted) {
                 mKeyguardSessionStarted = false;
-                endSession(SESSION_KEYGUARD);
+                endSession(SESSION_KEYGUARD,
+                        SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY);
             }
         }
     };
@@ -200,4 +213,22 @@
 
         return "unknownType=" + sessionType;
     }
+
+    enum SessionUiEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "A keyguard session ended due to the keyguard going away.")
+        KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY(1354),
+
+        @UiEvent(doc = "A keyguard session ended due to display going to sleep.")
+        KEYGUARD_SESSION_END_GOING_TO_SLEEP(1355);
+
+        private final int mId;
+        SessionUiEvent(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e204def..3775e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -367,13 +367,13 @@
 
     /**
      * Provides a {@link LogBuffer} for use by
-     *  {@link com.android.keyguard.faceauth.KeyguardFaceAuthManagerImpl}.
+     *  {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}.
      */
     @Provides
     @SysUISingleton
     @FaceAuthLog
     public static LogBuffer provideFaceAuthLog(LogBufferFactory factory) {
-        return factory.create("KeyguardFaceAuthManagerLog", 300);
+        return factory.create("DeviceEntryFaceAuthRepositoryLog", 300);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index f92a5ab..731bb2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -66,6 +66,8 @@
 
     protected final MediaOutputController mController;
 
+    private static final int UNMUTE_DEFAULT_VOLUME = 2;
+
     Context mContext;
     View mHolderView;
     boolean mIsDragging;
@@ -193,10 +195,6 @@
             mTwoLineTitleText.setTextColor(mController.getColorItemContent());
             if (mController.isAdvancedLayoutSupported()) {
                 mVolumeValueText.setTextColor(mController.getColorItemContent());
-                mTitleIcon.setOnTouchListener(((v, event) -> {
-                    mSeekBar.dispatchTouchEvent(event);
-                    return false;
-                }));
             }
             mSeekBar.setProgressTintList(
                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
@@ -546,13 +544,21 @@
         private void enableSeekBar(MediaDevice device) {
             mSeekBar.setEnabled(true);
             mSeekBar.setOnTouchListener((v, event) -> false);
-            if (mController.isAdvancedLayoutSupported()) {
-                updateIconAreaClickListener((v) -> {
+            updateIconAreaClickListener((v) -> {
+                if (device.getCurrentVolume() == 0) {
+                    mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
+                    updateUnmutedVolumeIcon();
+                    mTitleIcon.setOnTouchListener(((iconV, event) -> false));
+                } else {
                     mSeekBar.resetVolume();
                     mController.adjustVolume(device, 0);
                     updateMutedVolumeIcon();
-                });
-            }
+                    mTitleIcon.setOnTouchListener(((iconV, event) -> {
+                        mSeekBar.dispatchTouchEvent(event);
+                        return false;
+                    }));
+                }
+            });
         }
 
         protected void setUpDeviceIcon(MediaDevice device) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 44c718f..e8ef612 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1732,6 +1732,11 @@
         final int gestureHeight = userContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_gesture_height);
         final boolean handlingGesture = mEdgeBackGestureHandler.isHandlingGestures();
+        final InsetsFrameProvider mandatoryGestureProvider = new InsetsFrameProvider(
+                mInsetsSourceOwner, 0, WindowInsets.Type.mandatorySystemGestures());
+        if (handlingGesture) {
+            mandatoryGestureProvider.setInsetsSize(Insets.of(0, 0, 0, gestureHeight));
+        }
         final int gestureInsetsLeft = handlingGesture
                 ? mEdgeBackGestureHandler.getEdgeWidthLeft() + safeInsetsLeft : 0;
         final int gestureInsetsRight = handlingGesture
@@ -1739,9 +1744,7 @@
         return new InsetsFrameProvider[] {
                 navBarProvider,
                 tappableElementProvider,
-                new InsetsFrameProvider(
-                        mInsetsSourceOwner, 0, WindowInsets.Type.mandatorySystemGestures())
-                        .setInsetsSize(Insets.of(0, 0, 0, gestureHeight)),
+                mandatoryGestureProvider,
                 new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.systemGestures())
                         .setSource(InsetsFrameProvider.SOURCE_DISPLAY)
                         .setInsetsSize(Insets.of(gestureInsetsLeft, 0, 0, 0)),
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index f5c0a94..334c70b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -296,7 +296,8 @@
 
         // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
         // was used to start the note task.
-        putExtra(Intent.EXTRA_USE_STYLUS_MODE, true)
+        val useStylusMode = info.entryPoint != NoteTaskEntryPoint.KEYBOARD_SHORTCUT
+        putExtra(Intent.EXTRA_USE_STYLUS_MODE, useStylusMode)
 
         addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
         // We should ensure the note experience can be opened both as a full screen (lockscreen)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index 2fa8f9a..fae325c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -25,7 +25,8 @@
  * An entry point represents where the note task has ben called from. In rare cases, it may
  * represent a "re-entry" (i.e., [APP_CLIPS]).
  */
-enum class NoteTaskEntryPoint {
+enum class
+NoteTaskEntryPoint {
 
     /** @see [LaunchNoteTaskActivity] */
     WIDGET_PICKER_SHORTCUT,
@@ -38,4 +39,7 @@
 
     /** @see [AppClipsTrampolineActivity] */
     APP_CLIPS,
+
+    /** @see [NoteTaskInitializer.callbacks] */
+    KEYBOARD_SHORTCUT,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
index 16dd16e..48a5933 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
@@ -18,6 +18,7 @@
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
+import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
 import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
 import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
 import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
@@ -51,6 +52,7 @@
                 WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT
                 QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
                 APP_CLIPS -> return
+                KEYBOARD_SHORTCUT -> return
                 null -> return
             }
         uiEventLogger.log(event, info.uid, info.packageName)
@@ -70,6 +72,7 @@
                 WIDGET_PICKER_SHORTCUT -> return
                 QUICK_AFFORDANCE -> return
                 APP_CLIPS -> return
+                KEYBOARD_SHORTCUT -> return
                 null -> return
             }
         uiEventLogger.log(event, info.uid, info.packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 04ed08b..23ee13b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -41,9 +41,12 @@
     @VisibleForTesting
     val callbacks =
         object : CommandQueue.Callbacks {
-            override fun handleSystemKey(keyCode: Int) {
-                if (keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
+            override fun handleSystemKey(key: KeyEvent) {
+                if (key.keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
                     controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON)
+                } else if (key.keyCode == KeyEvent.KEYCODE_N && key.isMetaPressed &&
+                        key.isCtrlPressed) {
+                    controller.showNoteTask(NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5355865..0641eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -47,7 +47,6 @@
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_INFORM_JOB_SCHEDULER_OF_PENDING_APP_STOP
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS
@@ -80,8 +79,6 @@
 
 /** A controller for the dealing with services running in the foreground. */
 interface FgsManagerController {
-    /** Whether the TaskManager (and therefore this controller) is actually available. */
-    val isAvailable: StateFlow<Boolean>
 
     /** The number of packages with a service running in the foreground. */
     val numRunningPackages: Int
@@ -155,7 +152,6 @@
 
     companion object {
         private const val INTERACTION_JANK_TAG = "active_background_apps"
-        private const val DEFAULT_TASK_MANAGER_ENABLED = true
         private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
         private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
         private const val DEFAULT_TASK_MANAGER_SHOW_USER_VISIBLE_JOBS = true
@@ -165,9 +161,6 @@
     override var newChangesSinceDialogWasDismissed = false
         private set
 
-    val _isAvailable = MutableStateFlow(false)
-    override val isAvailable: StateFlow<Boolean> = _isAvailable.asStateFlow()
-
     val _showFooterDot = MutableStateFlow(false)
     override val showFooterDot: StateFlow<Boolean> = _showFooterDot.asStateFlow()
 
@@ -264,7 +257,6 @@
                 NAMESPACE_SYSTEMUI,
                 backgroundExecutor
             ) {
-                _isAvailable.value = it.getBoolean(TASK_MANAGER_ENABLED, _isAvailable.value)
                 _showFooterDot.value =
                     it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, _showFooterDot.value)
                 showStopBtnForUserAllowlistedApps = it.getBoolean(
@@ -280,11 +272,6 @@
                     TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
                     informJobSchedulerOfPendingAppStop)
             }
-
-            _isAvailable.value = deviceConfigProxy.getBoolean(
-                NAMESPACE_SYSTEMUI,
-                TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED
-            )
             _showFooterDot.value = deviceConfigProxy.getBoolean(
                 NAMESPACE_SYSTEMUI,
                 TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 6be74a0..ce690e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -26,6 +26,7 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.util.leak.GarbageMonitor;
 
@@ -34,7 +35,7 @@
 import java.util.Collection;
 import java.util.List;
 
-public interface QSHost extends PanelInteractor {
+public interface QSHost extends PanelInteractor, CustomTileAddedRepository {
     String TILES_SETTING = Settings.Secure.QS_TILES;
     int POSITION_AT_END = -1;
 
@@ -102,9 +103,6 @@
     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/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 958fa71..964fe71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
 import dagger.Binds
@@ -46,5 +48,19 @@
                 qsHost
             }
         }
+
+        @Provides
+        @JvmStatic
+        fun provideCustomTileAddedRepository(
+            featureFlags: FeatureFlags,
+            qsHost: QSHost,
+            customTileAddedRepository: CustomTileAddedSharedPrefsRepository
+        ): CustomTileAddedRepository {
+            return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+                customTileAddedRepository
+            } else {
+                qsHost
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 9f93e49..7a10a27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -33,6 +33,7 @@
 
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.settings.UserTracker;
 
 import java.util.List;
@@ -59,6 +60,7 @@
     private final TileLifecycleManager mStateManager;
     private final Handler mHandler;
     private final UserTracker mUserTracker;
+    private final CustomTileAddedRepository mCustomTileAddedRepository;
     private boolean mBindRequested;
     private boolean mBindAllowed;
     private boolean mBound;
@@ -72,9 +74,10 @@
     private boolean mStarted = false;
 
     TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
-            BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) {
-        this(tileServices, handler, userTracker, new TileLifecycleManager(handler,
-                tileServices.getContext(), tileServices,
+            BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
+            CustomTileAddedRepository customTileAddedRepository) {
+        this(tileServices, handler, userTracker, customTileAddedRepository,
+                new TileLifecycleManager(handler, tileServices.getContext(), tileServices,
                 new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
                 new Intent(TileService.ACTION_QS_TILE).setComponent(component),
                 userTracker.getUserHandle()));
@@ -82,11 +85,13 @@
 
     @VisibleForTesting
     TileServiceManager(TileServices tileServices, Handler handler, UserTracker userTracker,
+            CustomTileAddedRepository customTileAddedRepository,
             TileLifecycleManager tileLifecycleManager) {
         mServices = tileServices;
         mHandler = handler;
         mStateManager = tileLifecycleManager;
         mUserTracker = userTracker;
+        mCustomTileAddedRepository = customTileAddedRepository;
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -111,8 +116,8 @@
         mStarted = true;
         ComponentName component = mStateManager.getComponent();
         final int userId = mStateManager.getUserId();
-        if (!mServices.getHost().isTileAdded(component, userId)) {
-            mServices.getHost().setTileAdded(component, userId, true);
+        if (!mCustomTileAddedRepository.isTileAdded(component, userId)) {
+            mCustomTileAddedRepository.setTileAdded(component, userId, true);
             mStateManager.onTileAdded();
             mStateManager.flushMessagesAndUnbind();
         }
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 42536fe..121955c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -41,6 +41,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
@@ -77,6 +78,7 @@
     private final UserTracker mUserTracker;
     private final StatusBarIconController mStatusBarIconController;
     private final PanelInteractor mPanelInteractor;
+    private final CustomTileAddedRepository mCustomTileAddedRepository;
 
     private int mMaxBound = DEFAULT_MAX_BOUND;
 
@@ -89,7 +91,8 @@
             KeyguardStateController keyguardStateController,
             CommandQueue commandQueue,
             StatusBarIconController statusBarIconController,
-            PanelInteractor panelInteractor) {
+            PanelInteractor panelInteractor,
+            CustomTileAddedRepository customTileAddedRepository) {
         mHost = host;
         mKeyguardStateController = keyguardStateController;
         mContext = mHost.getContext();
@@ -101,6 +104,7 @@
         mStatusBarIconController = statusBarIconController;
         mCommandQueue.addCallback(mRequestListeningCallback);
         mPanelInteractor = panelInteractor;
+        mCustomTileAddedRepository = customTileAddedRepository;
     }
 
     public Context getContext() {
@@ -128,7 +132,7 @@
     protected TileServiceManager onCreateTileService(ComponentName component,
             BroadcastDispatcher broadcastDispatcher) {
         return new TileServiceManager(this, mHandlerProvider.get(), component,
-                broadcastDispatcher, mUserTracker);
+                broadcastDispatcher, mUserTracker, mCustomTileAddedRepository);
     }
 
     public void freeService(CustomTile tile, TileServiceManager service) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
index 37a9c40..bd9d70c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
@@ -32,8 +32,6 @@
 interface ForegroundServicesRepository {
     /**
      * The number of packages with a service running in the foreground.
-     *
-     * Note that this will be equal to 0 if [FgsManagerController.isAvailable] is false.
      */
     val foregroundServicesCount: Flow<Int>
 
@@ -52,32 +50,24 @@
     fgsManagerController: FgsManagerController,
 ) : ForegroundServicesRepository {
     override val foregroundServicesCount: Flow<Int> =
-        fgsManagerController.isAvailable
-            .flatMapLatest { isAvailable ->
-                if (!isAvailable) {
-                    return@flatMapLatest flowOf(0)
+            conflatedCallbackFlow<Int> {
+                fun updateState(numberOfPackages: Int) {
+                    trySendWithFailureLogging(numberOfPackages, TAG)
                 }
 
-                conflatedCallbackFlow {
-                    fun updateState(numberOfPackages: Int) {
-                        trySendWithFailureLogging(numberOfPackages, TAG)
-                    }
-
-                    val listener =
+                val listener =
                         object : FgsManagerController.OnNumberOfPackagesChangedListener {
                             override fun onNumberOfPackagesChanged(numberOfPackages: Int) {
                                 updateState(numberOfPackages)
                             }
                         }
 
-                    fgsManagerController.addOnNumberOfPackagesChangedListener(listener)
-                    updateState(fgsManagerController.numRunningPackages)
-                    awaitClose {
-                        fgsManagerController.removeOnNumberOfPackagesChangedListener(listener)
-                    }
+                fgsManagerController.addOnNumberOfPackagesChangedListener(listener)
+                updateState(fgsManagerController.numRunningPackages)
+                awaitClose {
+                    fgsManagerController.removeOnNumberOfPackagesChangedListener(listener)
                 }
-            }
-            .distinctUntilChanged()
+            }.distinctUntilChanged()
 
     override val hasNewChanges: Flow<Boolean> =
         fgsManagerController.showFooterDot.flatMapLatest { showFooterDot ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt
new file mode 100644
index 0000000..7fc906b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.ComponentName
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserFileManager
+import javax.inject.Inject
+
+/**
+ * Repository for keeping track of whether a given [CustomTile] [ComponentName] has been added to
+ * the set of current tiles for a user. This is used to determine when lifecycle methods in
+ * `TileService` about the tile being added/removed need to be called.
+ */
+interface CustomTileAddedRepository {
+    /**
+     * Check if a particular [CustomTile] associated with [componentName] has been added for
+     * [userId] and has not been removed since.
+     */
+    fun isTileAdded(componentName: ComponentName, userId: Int): Boolean
+
+    /**
+     * Persists whether a particular [CustomTile] associated with [componentName] has been added and
+     * it's currently in the set of selected tiles for [userId].
+     */
+    fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean)
+}
+
+@SysUISingleton
+class CustomTileAddedSharedPrefsRepository
+@Inject
+constructor(private val userFileManager: UserFileManager) : CustomTileAddedRepository {
+
+    override fun isTileAdded(componentName: ComponentName, userId: Int): Boolean {
+        return userFileManager
+            .getSharedPreferences(TILES, 0, userId)
+            .getBoolean(componentName.flattenToString(), false)
+    }
+
+    override fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean) {
+        userFileManager
+            .getSharedPreferences(TILES, 0, userId)
+            .edit()
+            .putBoolean(componentName.flattenToString(), added)
+            .apply()
+    }
+
+    companion object {
+        private const val TILES = "tiles_prefs"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 3090b79..4a31998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -496,7 +496,7 @@
         }
 
         // Colors
-        if (state.state != lastState || state.disabledByPolicy || lastDisabledByPolicy) {
+        if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) {
             singleAnimator.cancel()
             mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
                     state.spec,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b83397..90e31af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -336,7 +336,8 @@
         @Override
         public void expandNotificationPanel() {
             verifyCallerAndClearCallingIdentity("expandNotificationPanel",
-                    () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+                    () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
+                            KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index a9af1a2..6f85c45 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -44,7 +44,6 @@
 import android.app.Notification;
 import android.app.assist.AssistContent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -525,39 +524,6 @@
                 mWindowManager.getCurrentWindowMetrics().getWindowInsets());
     }
 
-    @MainThread
-    void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
-            RequestCallback requestCallback) {
-        Assert.isMainThread();
-        mCurrentRequestCallback = requestCallback;
-        takeScreenshotInternal(topComponent, finisher, getFullScreenRect());
-    }
-
-    @MainThread
-    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
-            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
-            Consumer<Uri> finisher, RequestCallback requestCallback) {
-        Assert.isMainThread();
-        if (screenshot == null) {
-            Log.e(TAG, "Got null bitmap from screenshot message");
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            requestCallback.reportError();
-            return;
-        }
-
-        boolean showFlash = false;
-        if (screenshotScreenBounds == null
-                || !aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
-            showFlash = true;
-            visibleInsets = Insets.NONE;
-            screenshotScreenBounds = new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight());
-        }
-        mCurrentRequestCallback = requestCallback;
-        saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent,
-                showFlash, UserHandle.of(userId));
-    }
-
     /**
      * Clears current screenshot
      */
@@ -695,103 +661,6 @@
         setContentView(mScreenshotView);
     }
 
-    /**
-     * Takes a screenshot of the current display and shows an animation.
-     */
-    private void takeScreenshotInternal(ComponentName topComponent, Consumer<Uri> finisher,
-            Rect crop) {
-        mScreenshotTakenInPortrait =
-                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
-
-        // copy the input Rect, since SurfaceControl.screenshot can mutate it
-        Rect screenRect = new Rect(crop);
-        Bitmap screenshot = mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(),
-                crop);
-
-        if (screenshot == null) {
-            Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            if (mCurrentRequestCallback != null) {
-                mCurrentRequestCallback.reportError();
-            }
-            return;
-        }
-
-        saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true,
-                Process.myUserHandle());
-
-        mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
-                ClipboardOverlayController.SELF_PERMISSION);
-    }
-
-    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
-            Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
-        withWindowAttached(() -> {
-            if (mUserManager.isManagedProfile(owner.getIdentifier())) {
-                mScreenshotView.announceForAccessibility(mContext.getResources().getString(
-                        R.string.screenshot_saving_work_profile_title));
-            } else {
-                mScreenshotView.announceForAccessibility(
-                        mContext.getResources().getString(R.string.screenshot_saving_title));
-            }
-        });
-
-        mScreenshotView.reset();
-
-        if (mScreenshotView.isAttachedToWindow()) {
-            // if we didn't already dismiss for another reason
-            if (!mScreenshotView.isDismissing()) {
-                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0, mPackageName);
-            }
-            if (DEBUG_WINDOW) {
-                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
-                        + "(dismissing=" + mScreenshotView.isDismissing() + ")");
-            }
-        }
-        mPackageName = topComponent == null ? "" : topComponent.getPackageName();
-        mScreenshotView.setPackageName(mPackageName);
-
-        mScreenshotView.updateOrientation(
-                mWindowManager.getCurrentWindowMetrics().getWindowInsets());
-
-        mScreenBitmap = screenshot;
-
-        if (!isUserSetupComplete(owner)) {
-            Log.w(TAG, "User setup not complete, displaying toast only");
-            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
-            // and sharing shouldn't be exposed to the user.
-            saveScreenshotAndToast(owner, finisher);
-            return;
-        }
-
-        // Optimizations
-        mScreenBitmap.setHasAlpha(false);
-        mScreenBitmap.prepareToDraw();
-
-        saveScreenshotInWorkerThread(owner, finisher, this::showUiOnActionsReady,
-                this::showUiOnQuickShareActionReady);
-
-        // The window is focusable by default
-        setWindowFocusable(true);
-        mScreenshotView.requestFocus();
-
-        enqueueScrollCaptureRequest(owner);
-
-        attachWindow();
-        prepareAnimation(screenRect, showFlash, () -> {
-            mMessageContainerController.onScreenshotTaken(owner);
-        });
-
-        mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
-                mContext.getDrawable(R.drawable.overlay_badge_background), owner));
-        mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
-        // ignore system bar insets for the purpose of window layout
-        mWindow.getDecorView().setOnApplyWindowInsetsListener(
-                (v, insets) -> WindowInsets.CONSUMED);
-        mScreenshotHandler.cancelTimeout(); // restarted after animation
-    }
-
     private void prepareAnimation(Rect screenRect, boolean showFlash,
             Runnable onAnimationComplete) {
         mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f3d2828..1cdad83 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -36,9 +36,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -49,7 +46,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
-import android.view.WindowManager;
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -58,7 +54,6 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -222,30 +217,17 @@
             return;
         }
 
-        if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA_REFACTOR)) {
-            Log.d(TAG, "Processing screenshot data");
-            ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
-            try {
-                mProcessor.processAsync(screenshotData,
-                        (data) -> dispatchToController(data, onSaved, callback));
-            } catch (IllegalStateException e) {
-                Log.e(TAG, "Failed to process screenshot request!", e);
-                logFailedRequest(request);
-                mNotificationsController.notifyScreenshotError(
-                        R.string.screenshot_failed_to_capture_text);
-                callback.reportError();
-            }
-        } else {
-            try {
-                mProcessor.processAsync(request,
-                        (r) -> dispatchToController(r, onSaved, callback));
-            } catch (IllegalStateException e) {
-                Log.e(TAG, "Failed to process screenshot request!", e);
-                logFailedRequest(request);
-                mNotificationsController.notifyScreenshotError(
-                        R.string.screenshot_failed_to_capture_text);
-                callback.reportError();
-            }
+        Log.d(TAG, "Processing screenshot data");
+        ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
+        try {
+            mProcessor.processAsync(screenshotData,
+                    (data) -> dispatchToController(data, onSaved, callback));
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Failed to process screenshot request!", e);
+            logFailedRequest(request);
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            callback.reportError();
         }
     }
 
@@ -257,38 +239,6 @@
         mScreenshot.handleScreenshot(screenshot, uriConsumer, callback);
     }
 
-    private void dispatchToController(ScreenshotRequest request,
-            Consumer<Uri> uriConsumer, RequestCallback callback) {
-        ComponentName topComponent = request.getTopComponent();
-        String packageName = topComponent == null ? "" : topComponent.getPackageName();
-        mUiEventLogger.log(
-                ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName);
-
-        switch (request.getType()) {
-            case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
-                if (DEBUG_SERVICE) {
-                    Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
-                }
-                mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, callback);
-                break;
-            case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
-                if (DEBUG_SERVICE) {
-                    Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
-                }
-                Bitmap screenshot = request.getBitmap();
-                Rect screenBounds = request.getBoundsInScreen();
-                Insets insets = request.getInsets();
-                int taskId = request.getTaskId();
-                int userId = request.getUserId();
-
-                mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
-                        taskId, userId, topComponent, uriConsumer, callback);
-                break;
-            default:
-                Log.wtf(TAG, "Invalid screenshot option: " + request.getType());
-        }
-    }
-
     private void logFailedRequest(ScreenshotRequest request) {
         ComponentName topComponent = request.getTopComponent();
         String packageName = topComponent == null ? "" : topComponent.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 0318fa5..2f4cc14 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -283,11 +283,15 @@
                 }
                 mLockIconViewController.onTouchEvent(
                         ev,
-                        () -> mService.wakeUpIfDozing(
-                                mClock.uptimeMillis(),
-                                mView,
-                                "LOCK_ICON_TOUCH",
-                                PowerManager.WAKE_REASON_GESTURE)
+                        /* onGestureDetectedRunnable */
+                        () -> {
+                            mService.userActivity();
+                            mService.wakeUpIfDozing(
+                                    mClock.uptimeMillis(),
+                                    mView,
+                                    "LOCK_ICON_TOUCH",
+                                    PowerManager.WAKE_REASON_GESTURE);
+                        }
                 );
 
                 // In case we start outside of the view bounds (below the status bar), we need to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index fbb51ae..07b6869 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager
 import android.content.res.Resources
 import android.os.SystemProperties
+import android.os.Trace
 import android.util.IndentingPrintWriter
 import android.util.MathUtils
 import android.view.CrossWindowBlurListeners
@@ -42,7 +43,7 @@
 ) : Dumpable {
     val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
     val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
-
+    private val traceCookie = System.identityHashCode(this)
     private var lastAppliedBlur = 0
 
     init {
@@ -85,10 +86,13 @@
             if (supportsBlursOnWindows()) {
                 it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
                 if (lastAppliedBlur == 0 && radius != 0) {
+                    Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME,
+                            EARLY_WAKEUP_SLICE_NAME, traceCookie)
                     it.setEarlyWakeupStart()
                 }
                 if (lastAppliedBlur != 0 && radius == 0) {
                     it.setEarlyWakeupEnd()
+                    Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, traceCookie)
                 }
                 lastAppliedBlur = radius
             }
@@ -125,4 +129,9 @@
             it.println("isHighEndGfx: ${ActivityManager.isHighEndGfx()}")
         }
     }
+
+    companion object {
+        const val TRACK_NAME = "BlurUtils"
+        const val EARLY_WAKEUP_SLICE_NAME = "eEarlyWakeup"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c435799..fb4feb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,6 +53,7 @@
 import android.os.RemoteException;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -302,7 +303,7 @@
         default void remQsTile(ComponentName tile) { }
         default void clickTile(ComponentName tile) { }
 
-        default void handleSystemKey(int arg1) { }
+        default void handleSystemKey(KeyEvent arg1) { }
         default void showPinningEnterExitToast(boolean entering) { }
         default void showPinningEscapeToast() { }
         default void handleShowGlobalActionsMenu() { }
@@ -891,9 +892,9 @@
     }
 
     @Override
-    public void handleSystemKey(int key) {
+    public void handleSystemKey(KeyEvent key) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key, 0).sendToTarget();
+            mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key).sendToTarget();
         }
     }
 
@@ -1534,7 +1535,7 @@
                     break;
                 case MSG_HANDLE_SYSTEM_KEY:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).handleSystemKey(msg.arg1);
+                        mCallbacks.get(i).handleSystemKey((KeyEvent) msg.obj);
                     }
                     break;
                 case MSG_SHOW_GLOBAL_ACTIONS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 9272f06..0195d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -157,7 +157,8 @@
         if (animationAdapter != null) {
             if (ENABLE_SHELL_TRANSITIONS) {
                 options = ActivityOptions.makeRemoteTransition(
-                        RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter));
+                        RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter,
+                                "SysUILaunch"));
             } else {
                 options = ActivityOptions.makeRemoteAnimation(animationAdapter);
             }
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 e776015..8b6617b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -308,7 +308,7 @@
      * settings. Down action closes the entire panel.
      */
     @Override
-    public void handleSystemKey(int key) {
+    public void handleSystemKey(KeyEvent key) {
         if (CentralSurfaces.SPEW) {
             Log.d(CentralSurfaces.TAG, "handleNavigationKey: " + key);
         }
@@ -320,11 +320,11 @@
         // Panels are not available in setup
         if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
 
-        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
+        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key.getKeyCode()) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
             mNotificationPanelViewController.collapse(
                     false /* delayed */, 1.0f /* speedUpFactor */);
-        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
+        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key.getKeyCode()) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
             if (mNotificationPanelViewController.isFullyCollapsed()) {
                 if (mVibrateOnOpening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index c817466..b303151 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -85,16 +85,17 @@
 
     /**
      * Called when keyguard is about to be displayed and allows to perform custom animation
+     *
+     * @return A handle that can be used for cancelling the animation, if necessary
      */
-    fun animateInKeyguard(keyguardView: View, after: Runnable) =
-        animations.firstOrNull {
+    fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
+        animations.forEach {
             if (it.shouldAnimateInKeyguard()) {
-                it.animateInKeyguard(keyguardView, after)
-                true
-            } else {
-                false
+                return@animateInKeyguard it.animateInKeyguard(keyguardView, after)
             }
         }
+        return null
+    }
 
     /**
      * If returns true it will disable propagating touches to apps and keyguard
@@ -211,7 +212,10 @@
     fun onAlwaysOnChanged(alwaysOn: Boolean) {}
 
     fun shouldAnimateInKeyguard(): Boolean = false
-    fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+    fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
+        after.run()
+        return null
+    }
 
     fun shouldDelayKeyguardShow(): Boolean = false
     fun isKeyguardShowDelayed(): Boolean = false
@@ -224,3 +228,7 @@
     fun shouldAnimateDozingChange(): Boolean = true
     fun shouldAnimateClockChange(): Boolean = true
 }
+
+interface AnimatorHandle {
+    fun cancel()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 118bfc5..deb0414 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -160,7 +160,7 @@
      * Animates in the provided keyguard view, ending in the same position that it will be in on
      * AOD.
      */
-    override fun animateInKeyguard(keyguardView: View, after: Runnable) {
+    override fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle {
         shouldAnimateInKeyguard = false
         keyguardView.alpha = 0f
         keyguardView.visibility = View.VISIBLE
@@ -175,11 +175,36 @@
         // We animate the Y properly separately using the PropertyAnimator, as the panel
         // view also needs to update the end position.
         PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y)
-        PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY,
-                AnimationProperties().setDuration(duration.toLong()),
-                true /* animate */)
 
-        keyguardView.animate()
+        // Start the animation on the next frame using Choreographer APIs. animateInKeyguard() is
+        // called while the system is busy processing lots of requests, so delaying the animation a
+        // frame will mitigate jank. In the event the animation is cancelled before the next frame
+        // is called, this callback will be removed
+        val keyguardAnimator = keyguardView.animate()
+        val nextFrameCallback = TraceUtils.namedRunnable("startAnimateInKeyguard") {
+            PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY,
+                    AnimationProperties().setDuration(duration.toLong()),
+                    true /* animate */)
+            keyguardAnimator.start()
+        }
+        DejankUtils.postAfterTraversal(nextFrameCallback)
+        val animatorHandle = object : AnimatorHandle {
+            private var hasCancelled = false
+            override fun cancel() {
+                if (!hasCancelled) {
+                    DejankUtils.removeCallbacks(nextFrameCallback)
+                    // If we're cancelled, reset state flags/listeners. The end action above
+                    // will not be called, which is what we want since that will finish the
+                    // screen off animation and show the lockscreen, which we don't want if we
+                    // were cancelled.
+                    aodUiAnimationPlaying = false
+                    decidedToAnimateGoingToSleep = null
+                    keyguardView.animate().setListener(null)
+                    hasCancelled = true
+                }
+            }
+        }
+        keyguardAnimator
                 .setDuration(duration.toLong())
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(1f)
@@ -205,14 +230,7 @@
                 }
                 .setListener(object : AnimatorListenerAdapter() {
                     override fun onAnimationCancel(animation: Animator?) {
-                        // If we're cancelled, reset state flags/listeners. The end action above
-                        // will not be called, which is what we want since that will finish the
-                        // screen off animation and show the lockscreen, which we don't want if we
-                        // were cancelled.
-                        aodUiAnimationPlaying = false
-                        decidedToAnimateGoingToSleep = null
-                        keyguardView.animate().setListener(null)
-
+                        animatorHandle.cancel()
                         interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
                     }
 
@@ -222,7 +240,7 @@
                                 CUJ_SCREEN_OFF_SHOW_AOD)
                     }
                 })
-                .start()
+        return animatorHandle
     }
 
     override fun onStartedWakingUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index adfea80..eaa1455 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -37,6 +37,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl
+import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
+import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxyImpl
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
@@ -65,8 +67,7 @@
 
     @Binds abstract fun wifiRepository(impl: WifiRepositorySwitcher): WifiRepository
 
-    @Binds
-    abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
+    @Binds abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
 
     @Binds
     abstract fun mobileConnectionsRepository(
@@ -78,6 +79,11 @@
     @Binds abstract fun mobileMappingsProxy(impl: MobileMappingsProxyImpl): MobileMappingsProxy
 
     @Binds
+    abstract fun subscriptionManagerProxy(
+        impl: SubscriptionManagerProxyImpl
+    ): SubscriptionManagerProxy
+
+    @Binds
     abstract fun mobileIconsInteractor(impl: MobileIconsInteractorImpl): MobileIconsInteractor
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 8c93bf7..45d50c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -65,6 +66,7 @@
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.withContext
 
@@ -76,6 +78,7 @@
 constructor(
     connectivityRepository: ConnectivityRepository,
     private val subscriptionManager: SubscriptionManager,
+    private val subscriptionManagerProxy: SubscriptionManagerProxy,
     private val telephonyManager: TelephonyManager,
     private val logger: MobileInputLogger,
     @MobileSummaryLog private val tableLogger: TableLogBuffer,
@@ -195,7 +198,7 @@
     override val defaultDataSubId: StateFlow<Int> =
         broadcastDispatcher
             .broadcastFlow(
-                IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+                IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED),
             ) { intent, _ ->
                 intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
             }
@@ -204,14 +207,11 @@
                 tableLogger,
                 LOGGING_PREFIX,
                 columnName = "defaultSubId",
-                initialValue = SubscriptionManager.getDefaultDataSubscriptionId(),
+                initialValue = INVALID_SUBSCRIPTION_ID,
             )
+            .onStart { emit(subscriptionManagerProxy.getDefaultDataSubscriptionId()) }
             .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
-            .stateIn(
-                scope,
-                SharingStarted.WhileSubscribed(),
-                SubscriptionManager.getDefaultDataSubscriptionId()
-            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
 
     private val carrierConfigChangedEvent =
         broadcastDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt
new file mode 100644
index 0000000..22d0483
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/SubscriptionManagerProxy.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.util
+
+import android.telephony.SubscriptionManager
+import javax.inject.Inject
+
+interface SubscriptionManagerProxy {
+    fun getDefaultDataSubscriptionId(): Int
+}
+
+/** Injectable proxy class for [SubscriptionManager]'s static methods */
+class SubscriptionManagerProxyImpl @Inject constructor() : SubscriptionManagerProxy {
+    /** The system default data subscription id, or INVALID_SUBSCRIPTION_ID on error */
+    override fun getDefaultDataSubscriptionId() = SubscriptionManager.getDefaultDataSubscriptionId()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index cb561f6..d1bd73a 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -156,6 +156,7 @@
         } else if (isFolded && !isFoldHandled && alwaysOnEnabled && isDozing) {
             // Screen turning on for the first time after folding and we are already dozing
             // We should play the folding to AOD animation
+            isFoldHandled = true
 
             setAnimationState(playing = true)
             getShadeFoldAnimator().prepareFoldToAodAnimation()
@@ -190,8 +191,10 @@
             cancelAnimation?.run()
 
             // Post starting the animation to the next frame to avoid junk due to inset changes
-            cancelAnimation =
-                mainExecutor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
+            cancelAnimation = mainExecutor.executeDelayed(
+                startAnimationRunnable,
+                /* delayMillis= */ 0
+            )
             shouldPlayAnimation = false
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index bd60401..e492534 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -295,8 +295,8 @@
             @Override
             public void notifyExpandNotification() {
                 mSysUiMainExecutor.execute(
-                        () -> mCommandQueue.handleSystemKey(
-                                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+                        () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
+                                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
             }
         });
 
diff --git a/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg b/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg
new file mode 100644
index 0000000..68473ba
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg
Binary files differ
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 7ce2b1c..1ba9931 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -152,19 +152,16 @@
                 false);
     }
 
+
     @Test
     public void testReset() {
         mKeyguardAbsKeyInputViewController.reset();
         verify(mKeyguardMessageAreaController).setMessage("", false);
-        verify(mAbsKeyInputView).resetPasswordText(false, false);
-        verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt());
     }
 
     @Test
-    public void onResume_Reset() {
+    public void testResume() {
         mKeyguardAbsKeyInputViewController.onResume(KeyguardSecurityView.VIEW_REVEALED);
-        verify(mKeyguardMessageAreaController).setMessage("", false);
-        verify(mAbsKeyInputView).resetPasswordText(false, false);
         verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 6ae28b7..a8d5569 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -122,22 +122,8 @@
   }
 
   @Test
-  fun reset() {
-    mKeyguardPatternViewController.reset()
-    verify(mLockPatternView).setInStealthMode(anyBoolean())
-    verify(mLockPatternView).enableInput()
-    verify(mLockPatternView).setEnabled(true)
-    verify(mLockPatternView).clearPattern()
-    verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
-  }
-
-  @Test
   fun resume() {
     mKeyguardPatternViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
-    verify(mLockPatternView).setInStealthMode(anyBoolean())
-    verify(mLockPatternView).enableInput()
-    verify(mLockPatternView).setEnabled(true)
-    verify(mLockPatternView).clearPattern()
     verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
   }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index b73330f..65f8610 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -393,6 +393,45 @@
     }
 
     @Test
+    public void showNextSecurityScreenOrFinish_DeviceNotSecure() {
+        // GIVEN the current security method is SimPin
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+        // WHEN a request is made from the SimPin screens to show the next security method
+        when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None);
+        mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+                /* authenticated= */true,
+                TARGET_USER_ID,
+                /* bypassSecondaryLockScreen= */true,
+                SecurityMode.SimPin);
+
+        // THEN the next security method of None will dismiss keyguard.
+        verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void showNextSecurityScreenOrFinish_DeviceNotSecure_prevent_bypass_on() {
+        when(mFeatureFlags.isEnabled(Flags.PREVENT_BYPASS_KEYGUARD)).thenReturn(true);
+        // GIVEN the current security method is SimPin
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+        // WHEN a request is made from the SimPin screens to show the next security method
+        when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None);
+        mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+                /* authenticated= */true,
+                TARGET_USER_ID,
+                /* bypassSecondaryLockScreen= */true,
+                SecurityMode.SimPin);
+
+        // THEN the next security method of None will dismiss keyguard.
+        verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt());
+    }
+
+    @Test
     public void showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() {
         //GIVEN current security mode has been set to PIN
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.PIN);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 33f0ae5..b6287598 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -141,27 +141,6 @@
     }
 
     @Test
-    public void testUnlockIconShows_biometricUnlockedTrue() {
-        // GIVEN UDFPS sensor location is available
-        setupUdfps();
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        reset(mLockIconView);
-
-        // WHEN face auth's biometric running state changes
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-
-        // THEN the unlock icon is shown
-        verify(mLockIconView).setContentDescription(UNLOCKED_LABEL);
-    }
-
-    @Test
     public void testLockIconStartState() {
         // GIVEN lock icon state
         setupShowLockIcon();
@@ -268,27 +247,6 @@
     }
 
     @Test
-    public void lockIconShows_afterBiometricsCleared() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-
-        // WHEN biometrics are cleared
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardUpdateMonitorCallback.onBiometricsCleared();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setContentDescription(LOCKED_LABEL);
-    }
-
-    @Test
     public void lockIconShows_afterUnlockStateChanges() {
         // GIVEN lock icon controller is initialized and view is attached
         init(/* useMigrationFlag= */false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
index eb82956..353a7c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SystemSettings
@@ -34,6 +36,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 private const val ON: Int = 1
@@ -53,6 +59,9 @@
             .getResources()
             .getStringArray(com.android.settingslib.R.array.entryvalues_font_size)
 
+    @Captor
+    private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -61,7 +70,7 @@
         secureSettings = FakeSettings()
         backgroundExecutor = FakeExecutor(FakeSystemClock())
         fontScalingDialog =
-            FontScalingDialog(mContext, systemSettings, secureSettings, backgroundExecutor)
+            spy(FontScalingDialog(mContext, systemSettings, secureSettings, backgroundExecutor))
     }
 
     @Test
@@ -70,7 +79,7 @@
 
         val seekBar: SeekBar = fontScalingDialog.findViewById<SeekBar>(R.id.seekbar)!!
         val progress: Int = seekBar.getProgress()
-        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f)
 
         assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat())
 
@@ -91,7 +100,7 @@
         iconEndFrame.performClick()
         backgroundExecutor.runAllReady()
 
-        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f)
         assertThat(seekBar.getProgress()).isEqualTo(1)
         assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat())
 
@@ -112,7 +121,7 @@
         iconStartFrame.performClick()
         backgroundExecutor.runAllReady()
 
-        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f)
         assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2)
         assertThat(currentScale)
             .isEqualTo(fontSizeValueArray[fontSizeValueArray.size - 2].toFloat())
@@ -141,4 +150,64 @@
 
         fontScalingDialog.dismiss()
     }
+
+    @Test
+    fun dragSeekbar_systemFontSizeSettingsDoesNotChange() {
+        val slider: SeekBarWithIconButtonsView = spy(SeekBarWithIconButtonsView(mContext))
+        whenever(
+                fontScalingDialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)
+            )
+            .thenReturn(slider)
+        fontScalingDialog.show()
+        verify(slider).setOnSeekBarChangeListener(capture(seekBarChangeCaptor))
+        val seekBar: SeekBar = slider.findViewById(R.id.seekbar)!!
+
+        // Default seekbar progress for font size is 1, simulate dragging to 0 without
+        // releasing the finger.
+        seekBarChangeCaptor.value.onStartTrackingTouch(seekBar)
+        // Update seekbar progress. This will trigger onProgressChanged in the
+        // OnSeekBarChangeListener and the seekbar could get updated progress value
+        // in onStopTrackingTouch.
+        seekBar.progress = 0
+        backgroundExecutor.runAllReady()
+
+        // Verify that the scale of font size remains the default value 1.0f.
+        var systemScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f)
+        assertThat(systemScale).isEqualTo(1.0f)
+
+        // Simulate releasing the finger from the seekbar.
+        seekBarChangeCaptor.value.onStopTrackingTouch(seekBar)
+        backgroundExecutor.runAllReady()
+
+        // Verify that the scale of font size has been updated.
+        systemScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f)
+        assertThat(systemScale).isEqualTo(fontSizeValueArray[0].toFloat())
+
+        fontScalingDialog.dismiss()
+    }
+
+    @Test
+    fun dragSeekBar_createTextPreview() {
+        val slider: SeekBarWithIconButtonsView = spy(SeekBarWithIconButtonsView(mContext))
+        whenever(
+                fontScalingDialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)
+            )
+            .thenReturn(slider)
+        fontScalingDialog.show()
+        verify(slider).setOnSeekBarChangeListener(capture(seekBarChangeCaptor))
+        val seekBar: SeekBar = slider.findViewById(R.id.seekbar)!!
+
+        // Default seekbar progress for font size is 1, simulate dragging to 0 without
+        // releasing the finger
+        seekBarChangeCaptor.value.onStartTrackingTouch(seekBar)
+        seekBarChangeCaptor.value.onProgressChanged(
+            seekBar,
+            /* progress= */ 0,
+            /* fromUser= */ false
+        )
+        backgroundExecutor.runAllReady()
+
+        verify(fontScalingDialog).createTextPreview(/* index= */ 0)
+        fontScalingDialog.dismiss()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 6ddba0b..b41053c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -25,7 +25,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.timeout
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.junit.MockitoJUnit
@@ -49,17 +49,31 @@
     }
 
     @Test
-    fun testEnableDetector_shouldPostRunnable() {
+    fun testEnableDetector_expandWithTrack_shouldPostRunnable() {
         detector.enable(action)
         // simulate notification expand
         shadeExpansionStateManager.onPanelExpansionChanged(5566f, true, true, 5566f)
-        verify(action, timeout(5000).times(1)).run()
+        verify(action).run()
+    }
+
+    @Test
+    fun testEnableDetector_trackOnly_shouldPostRunnable() {
+        detector.enable(action)
+        // simulate notification expand
+        shadeExpansionStateManager.onPanelExpansionChanged(5566f, false, true, 5566f)
+        verify(action).run()
+    }
+
+    @Test
+    fun testEnableDetector_expandOnly_shouldPostRunnable() {
+        detector.enable(action)
+        // simulate notification expand
+        shadeExpansionStateManager.onPanelExpansionChanged(5566f, true, false, 5566f)
+        verify(action).run()
     }
 
     @Test
     fun testEnableDetector_shouldNotPostRunnable() {
-        var detector =
-            AuthDialogPanelInteractionDetector(shadeExpansionStateManager, mContext.mainExecutor)
         detector.enable(action)
         detector.disable()
         shadeExpansionStateManager.onPanelExpansionChanged(5566f, true, true, 5566f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 236384b..4ea9616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -32,6 +32,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -161,6 +162,7 @@
         }
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -176,6 +178,7 @@
         )
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -191,6 +194,7 @@
         }
 
         verify(controlsModelCallback, never()).onFirstChange()
+        verify(controlsModelCallback, never()).onChange()
     }
 
     @Test
@@ -207,6 +211,7 @@
         }
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -222,6 +227,7 @@
         )
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -236,5 +242,24 @@
         }
 
         verify(controlsModelCallback, never()).onFirstChange()
+        verify(controlsModelCallback, never()).onChange()
+    }
+
+    @Test
+    fun testAddSecondChange_callbacks() {
+        model.changeFavoriteStatus("${idPrefix}4", true)
+        model.changeFavoriteStatus("${idPrefix}5", true)
+
+        verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback, times(2)).onChange()
+    }
+
+    @Test
+    fun testRemoveSecondChange_callbacks() {
+        model.changeFavoriteStatus("${idPrefix}1", false)
+        model.changeFavoriteStatus("${idPrefix}3", false)
+
+        verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback, times(2)).onChange()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index 3b6f7d1..4210675 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -2,27 +2,33 @@
 
 import android.content.ComponentName
 import android.content.Intent
+import android.os.Bundle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -32,7 +38,15 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ControlsEditingActivityTest : SysuiTestCase() {
+
+    private companion object {
+        val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+        val TEST_STRUCTURE: CharSequence = "TestStructure"
+        val TEST_APP: CharSequence = "TestApp"
+    }
+
     private val uiExecutor = FakeExecutor(FakeSystemClock())
+    private val featureFlags = FakeFeatureFlags()
 
     @Mock lateinit var controller: ControlsControllerImpl
 
@@ -40,9 +54,6 @@
 
     @Mock lateinit var customIconCache: CustomIconCache
 
-    @Mock lateinit var uiController: ControlsUiController
-
-    private lateinit var controlsEditingActivity: ControlsEditingActivity_Factory
     private var latch: CountDownLatch = CountDownLatch(1)
 
     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
@@ -58,11 +69,11 @@
                 ) {
                 override fun create(intent: Intent?): TestableControlsEditingActivity {
                     return TestableControlsEditingActivity(
+                        featureFlags,
                         uiExecutor,
                         controller,
                         userTracker,
                         customIconCache,
-                        uiController,
                         mockDispatcher,
                         latch
                     )
@@ -75,19 +86,17 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        val intent = Intent()
-        intent.putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, "TestTitle")
-        val cname = ComponentName("TestPackageName", "TestClassName")
-        intent.putExtra(Intent.EXTRA_COMPONENT_NAME, cname)
-        activityRule.launchActivity(intent)
+
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
     }
 
     @Test
     fun testBackCallbackRegistrationAndUnregistration() {
+        launchActivity()
         // 1. ensure that launching the activity results in it registering a callback
         verify(mockDispatcher)
             .registerOnBackInvokedCallback(
-                ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
                 captureCallback.capture()
             )
         activityRule.finishActivity()
@@ -96,15 +105,102 @@
         verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
     }
 
-    public class TestableControlsEditingActivity(
-        private val executor: FakeExecutor,
-        private val controller: ControlsControllerImpl,
-        private val userTracker: UserTracker,
-        private val customIconCache: CustomIconCache,
-        private val uiController: ControlsUiController,
+    @Test
+    fun testNewFlowDisabled_addControlsButton_gone() {
+        with(launchActivity()) {
+            val addControlsButton = requireViewById<Button>(R.id.addControls)
+            assertThat(addControlsButton.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun testNewFlowEnabled_addControlsButton_visible() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity()) {
+            val addControlsButton = requireViewById<Button>(R.id.addControls)
+            assertThat(addControlsButton.visibility).isEqualTo(View.VISIBLE)
+            assertThat(addControlsButton.isEnabled).isTrue()
+        }
+    }
+
+    @Test
+    fun testNotLaunchFromFavoriting_saveButton_disabled() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity(isFromFavoriting = false)) {
+            val saveButton = requireViewById<Button>(R.id.done)
+            assertThat(saveButton.isEnabled).isFalse()
+        }
+    }
+
+    @Test
+    fun testLaunchFromFavoriting_saveButton_enabled() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity(isFromFavoriting = true)) {
+            val saveButton = requireViewById<Button>(R.id.done)
+            assertThat(saveButton.isEnabled).isTrue()
+        }
+    }
+
+    @Test
+    fun testNotFromFavoriting_addControlsPressed_launchesFavouriting() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity(isFromFavoriting = false)) {
+            val addControls = requireViewById<Button>(R.id.addControls)
+
+            activityRule.runOnUiThread { addControls.performClick() }
+
+            with(startActivityData!!.intent) {
+                assertThat(component)
+                    .isEqualTo(ComponentName(context, ControlsFavoritingActivity::class.java))
+                assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE))
+                    .isEqualTo(TEST_STRUCTURE)
+                assertThat(
+                        getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+                    )
+                    .isEqualTo(TEST_COMPONENT)
+                assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_APP))
+                    .isEqualTo(TEST_APP)
+                assertThat(getByteExtra(ControlsFavoritingActivity.EXTRA_SOURCE, -1))
+                    .isEqualTo(ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING)
+            }
+        }
+    }
+
+    private fun launchActivity(
+        componentName: ComponentName = TEST_COMPONENT,
+        structure: CharSequence = TEST_STRUCTURE,
+        isFromFavoriting: Boolean = false,
+        app: CharSequence = TEST_APP,
+    ): TestableControlsEditingActivity =
+        activityRule.launchActivity(
+            Intent().apply {
+                putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, isFromFavoriting)
+                putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, structure)
+                putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+                putExtra(ControlsEditingActivity.EXTRA_APP, app)
+            }
+        )
+
+    class TestableControlsEditingActivity(
+        featureFlags: FakeFeatureFlags,
+        executor: FakeExecutor,
+        controller: ControlsControllerImpl,
+        userTracker: UserTracker,
+        customIconCache: CustomIconCache,
         private val mockDispatcher: OnBackInvokedDispatcher,
         private val latch: CountDownLatch
-    ) : ControlsEditingActivity(executor, controller, userTracker, customIconCache, uiController) {
+    ) :
+        ControlsEditingActivity(
+            featureFlags,
+            executor,
+            controller,
+            userTracker,
+            customIconCache,
+        ) {
+
+        var startActivityData: StartActivityData? = null
+            private set
+
         override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
             return mockDispatcher
         }
@@ -114,5 +210,13 @@
             // ensures that test runner thread does not proceed until ui thread is done
             latch.countDown()
         }
+
+        override fun startActivity(intent: Intent) {
+            startActivityData = StartActivityData(intent, null)
+        }
+
+        override fun startActivity(intent: Intent, options: Bundle?) {
+            startActivityData = StartActivityData(intent, options)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 3655232..6884616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -1,30 +1,49 @@
 package com.android.systemui.controls.management
 
+import android.content.ComponentName
 import android.content.Intent
+import android.os.Bundle
+import android.service.controls.Control
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.controller.createLoadDataObject
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
+import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Answers
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -32,7 +51,19 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ControlsFavoritingActivityTest : SysuiTestCase() {
+
+    private companion object {
+        val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+        val TEST_CONTROL =
+            mock(Control::class.java, Answers.RETURNS_MOCKS)!!.apply {
+                whenever(structure).thenReturn(TEST_STRUCTURE)
+            }
+        val TEST_STRUCTURE: CharSequence = "TestStructure"
+        val TEST_APP: CharSequence = "TestApp"
+    }
+
     @Main private val executor: Executor = MoreExecutors.directExecutor()
+    private val featureFlags = FakeFeatureFlags()
 
     @Mock lateinit var controller: ControlsControllerImpl
 
@@ -40,13 +71,15 @@
 
     @Mock lateinit var userTracker: UserTracker
 
-    @Mock lateinit var uiController: ControlsUiController
-
-    private lateinit var controlsFavoritingActivity: ControlsFavoritingActivity_Factory
     private var latch: CountDownLatch = CountDownLatch(1)
 
     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
     @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+    @Captor
+    private lateinit var listingCallback:
+        ArgumentCaptor<ControlsListingController.ControlsListingCallback>
+    @Captor
+    private lateinit var controlsCallback: ArgumentCaptor<Consumer<ControlsController.LoadData>>
 
     @Rule
     @JvmField
@@ -58,11 +91,11 @@
                 ) {
                 override fun create(intent: Intent?): TestableControlsFavoritingActivity {
                     return TestableControlsFavoritingActivity(
+                        featureFlags,
                         executor,
                         controller,
                         listingController,
                         userTracker,
-                        uiController,
                         mockDispatcher,
                         latch
                     )
@@ -75,19 +108,18 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        val intent = Intent()
-        intent.putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
-        activityRule.launchActivity(intent)
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
     }
 
     // b/259549854 to root-cause and fix
     @FlakyTest
     @Test
     fun testBackCallbackRegistrationAndUnregistration() {
+        launchActivity()
         // 1. ensure that launching the activity results in it registering a callback
         verify(mockDispatcher)
             .registerOnBackInvokedCallback(
-                ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
                 captureCallback.capture()
             )
         activityRule.finishActivity()
@@ -96,22 +128,116 @@
         verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
     }
 
-    public class TestableControlsFavoritingActivity(
+    @Test
+    fun testNewFlowEnabled_buttons() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity()) {
+            verify(listingController).addCallback(listingCallback.capture())
+            listingCallback.value.onServicesUpdated(
+                listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
+            )
+
+            val rearrangeButton = requireViewById<Button>(R.id.rearrange)
+            assertThat(rearrangeButton.visibility).isEqualTo(View.VISIBLE)
+            assertThat(rearrangeButton.isEnabled).isFalse()
+            assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun testNewFlowDisabled_buttons() {
+        with(launchActivity()) {
+            verify(listingController).addCallback(listingCallback.capture())
+            activityRule.runOnUiThread {
+                listingCallback.value.onServicesUpdated(
+                    listOf(
+                        mock(ControlsServiceInfo::class.java),
+                        mock(ControlsServiceInfo::class.java)
+                    )
+                )
+            }
+
+            val rearrangeButton = requireViewById<Button>(R.id.rearrange)
+            assertThat(rearrangeButton.visibility).isEqualTo(View.GONE)
+            assertThat(rearrangeButton.isEnabled).isFalse()
+            assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun testNewFlowEnabled_rearrangePressed_savesAndlaunchesActivity() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity()) {
+            verify(listingController).addCallback(capture(listingCallback))
+            listingCallback.value.onServicesUpdated(
+                listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
+            )
+            verify(controller).loadForComponent(any(), capture(controlsCallback), any())
+            activityRule.runOnUiThread {
+                controlsCallback.value.accept(
+                    createLoadDataObject(
+                        listOf(ControlStatus(TEST_CONTROL, TEST_COMPONENT, true)),
+                        emptyList(),
+                    )
+                )
+                requireViewById<Button>(R.id.rearrange).performClick()
+            }
+
+            verify(controller).replaceFavoritesForStructure(any())
+            with(startActivityData!!.intent) {
+                assertThat(component)
+                    .isEqualTo(ComponentName(context, ControlsEditingActivity::class.java))
+                assertThat(
+                        getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+                    )
+                    .isEqualTo(TEST_COMPONENT)
+                assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_APP))
+                    .isEqualTo(TEST_APP)
+                assertThat(getBooleanExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, false))
+                    .isTrue()
+                assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_STRUCTURE))
+                    .isEqualTo("")
+            }
+        }
+    }
+
+    private fun launchActivity(
+        componentName: ComponentName = TEST_COMPONENT,
+        structure: CharSequence = TEST_STRUCTURE,
+        app: CharSequence = TEST_APP,
+        source: Byte = ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR,
+    ): TestableControlsFavoritingActivity =
+        activityRule.launchActivity(
+            Intent().apply {
+                putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+                putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure)
+                putExtra(ControlsFavoritingActivity.EXTRA_APP, app)
+                putExtra(ControlsFavoritingActivity.EXTRA_SOURCE, source)
+            }
+        )
+
+    class TestableControlsFavoritingActivity(
+        featureFlags: FeatureFlags,
         executor: Executor,
         controller: ControlsControllerImpl,
         listingController: ControlsListingController,
         userTracker: UserTracker,
-        uiController: ControlsUiController,
         private val mockDispatcher: OnBackInvokedDispatcher,
         private val latch: CountDownLatch
     ) :
         ControlsFavoritingActivity(
+            featureFlags,
             executor,
             controller,
             listingController,
             userTracker,
-            uiController
         ) {
+
+        var triedToFinish = false
+
+        var startActivityData: StartActivityData? = null
+            private set
+
         override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
             return mockDispatcher
         }
@@ -121,5 +247,17 @@
             // ensures that test runner thread does not proceed until ui thread is done
             latch.countDown()
         }
+
+        override fun startActivity(intent: Intent) {
+            startActivityData = StartActivityData(intent, null)
+        }
+
+        override fun startActivity(intent: Intent, options: Bundle?) {
+            startActivityData = StartActivityData(intent, options)
+        }
+
+        override fun animateExitAndFinish() {
+            triedToFinish = true
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
new file mode 100644
index 0000000..977e3ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.os.Bundle
+
+data class StartActivityData(val intent: Intent, val options: Bundle?)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 7f6e2ba..08427da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -399,7 +399,21 @@
     }
 
     @Test
-    public void testPause() {
+    public void testPauseWithNoActiveSessions() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        environment.updateLifecycle(observerOwnerPair -> {
+            observerOwnerPair.first.onPause(observerOwnerPair.second);
+        });
+
+        environment.verifyInputSessionDispose();
+    }
+
+    @Test
+    public void testDeferredPauseWithActiveSessions() {
         final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
 
         final Environment environment = new Environment(Stream.of(touchHandler)
@@ -417,14 +431,59 @@
         environment.publishInputEvent(event);
         verify(eventListener).onInputEvent(eq(event));
 
+        final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+
+        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
+
         environment.updateLifecycle(observerOwnerPair -> {
             observerOwnerPair.first.onPause(observerOwnerPair.second);
         });
 
+        verify(environment.mInputSession, never()).dispose();
+
+        // End session
+        touchSessionArgumentCaptor.getValue().pop();
+        environment.executeAll();
+
+        // Check to make sure the input session is now disposed.
         environment.verifyInputSessionDispose();
     }
 
     @Test
+    public void testDestroyWithActiveSessions() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        // Ensure session started
+        final InputChannelCompat.InputEventListener eventListener =
+                registerInputEventListener(touchHandler);
+
+        // First event will be missed since we register after the execution loop,
+        final InputEvent event = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(event);
+        verify(eventListener).onInputEvent(eq(event));
+
+        final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+
+        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
+
+        environment.updateLifecycle(observerOwnerPair -> {
+            observerOwnerPair.first.onDestroy(observerOwnerPair.second);
+        });
+
+        // Check to make sure the input session is now disposed.
+        environment.verifyInputSessionDispose();
+    }
+
+
+    @Test
     public void testPilfering() {
         final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class);
         final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
@@ -476,7 +535,7 @@
         environment.executeAll();
 
         environment.updateLifecycle(observerOwnerPair -> {
-            observerOwnerPair.first.onPause(observerOwnerPair.second);
+            observerOwnerPair.first.onDestroy(observerOwnerPair.second);
         });
 
         environment.executeAll();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
new file mode 100644
index 0000000..5704ef3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.dreams.touch;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+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;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ShadeTouchHandlerTest extends SysuiTestCase {
+    @Mock
+    CentralSurfaces mCentralSurfaces;
+
+    @Mock
+    NotificationPanelViewController mNotificationPanelViewController;
+
+    @Mock
+    DreamTouchHandler.TouchSession mTouchSession;
+
+    ShadeTouchHandler mTouchHandler;
+
+    private static final int TOUCH_HEIGHT = 20;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces),
+                TOUCH_HEIGHT);
+        when(mCentralSurfaces.getNotificationPanelViewController())
+                .thenReturn(mNotificationPanelViewController);
+    }
+
+    /**
+     * Verify that touches aren't handled when the bouncer is showing.
+     */
+    @Test
+    public void testInactiveOnBouncer() {
+        when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).pop();
+    }
+
+    /**
+     * Make sure {@link ShadeTouchHandler}
+     */
+    @Test
+    public void testTouchPilferingOnScroll() {
+        final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
+        final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
+
+        final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
+                ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
+
+        assertThat(gestureListenerArgumentCaptor.getValue()
+                .onScroll(motionEvent1, motionEvent2, 1, 1))
+                .isTrue();
+    }
+
+    /**
+     * Ensure touches are propagated to the {@link NotificationPanelViewController}.
+     */
+    @Test
+    public void testEventPropagation() {
+        final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+
+        final ArgumentCaptor<InputChannelCompat.InputEventListener>
+                inputEventListenerArgumentCaptor =
+                    ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
+        inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
+        verify(mNotificationPanelViewController).handleExternalTouch(motionEvent);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
new file mode 100644
index 0000000..ccd631e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -0,0 +1,346 @@
+package com.android.systemui.graphics
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.ImageDecoder
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.VectorDrawable
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+@RunWith(AndroidJUnit4::class)
+class ImageLoaderTest : SysuiTestCase() {
+
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+    private val imageLoader = ImageLoader(context, testDispatcher)
+
+    private lateinit var imgFile: File
+
+    @Before
+    fun setUp() {
+        val context = context.createPackageContext("com.android.systemui.tests", 0)
+        val bitmap =
+            BitmapFactory.decodeResource(
+                context.resources,
+                com.android.systemui.tests.R.drawable.romainguy_rockaway
+            )
+
+        imgFile = File.createTempFile("image", ".png", context.cacheDir)
+        imgFile.deleteOnExit()
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(imgFile))
+    }
+
+    @After
+    fun tearDown() {
+        imgFile.delete()
+    }
+
+    @Test
+    fun invalidResource_drawable_returnsNull() =
+        testScope.runTest { assertThat(imageLoader.loadDrawable(ImageLoader.Res(-1))).isNull() }
+
+    @Test
+    fun invalidResource_bitmap_returnsNull() =
+        testScope.runTest { assertThat(imageLoader.loadBitmap(ImageLoader.Res(-1))).isNull() }
+
+    @Test
+    fun invalidUri_returnsNull() =
+        testScope.runTest {
+            assertThat(imageLoader.loadBitmap(ImageLoader.Uri("this.is/bogus"))).isNull()
+        }
+
+    @Test
+    fun invalidFile_returnsNull() =
+        testScope.runTest {
+            assertThat(imageLoader.loadBitmap(ImageLoader.File("this is broken!"))).isNull()
+        }
+
+    @Test
+    fun invalidIcon_returnsNull() =
+        testScope.runTest {
+            assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
+        }
+
+    @Test
+    fun invalidIS_returnsNull() =
+        testScope.runTest {
+            assertThat(
+                    imageLoader.loadDrawable(
+                        ImageLoader.InputStream(ByteArrayInputStream(ByteArray(0)))
+                    )
+                )
+                .isNull()
+        }
+
+    @Test
+    fun validBitmapResource_loadDrawable_returnsBitmapDrawable() =
+        testScope.runTest {
+            val context = context.createPackageContext("com.android.systemui.tests", 0)
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    com.android.systemui.tests.R.drawable.romainguy_rockaway
+                )
+            assertThat(bitmap).isNotNull()
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.Res(
+                        com.android.systemui.tests.R.drawable.romainguy_rockaway,
+                        context
+                    )
+                )
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validBitmapResource_loadBitmap_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val loadedBitmap =
+                imageLoader.loadBitmap(ImageLoader.Res(R.drawable.dessert_zombiegingerbread))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validBitmapUri_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+
+            val uri =
+                "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+            val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Uri(uri))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validBitmapFile_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
+            val loadedBitmap = imageLoader.loadBitmap(ImageLoader.File(imgFile))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validInputStream_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
+            val loadedBitmap =
+                imageLoader.loadBitmap(ImageLoader.InputStream(FileInputStream(imgFile)))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validBitmapIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val loadedDrawable = imageLoader.loadDrawable(Icon.createWithBitmap(bitmap))
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validUriIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val uri =
+                "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+            val loadedDrawable = imageLoader.loadDrawable(Icon.createWithContentUri(Uri.parse(uri)))
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validDataIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val bos =
+                ByteArrayOutputStream(
+                    bitmap.byteCount * 2
+                ) // Compressed bitmap should be smaller than its source.
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
+
+            val array = bos.toByteArray()
+            val loadedDrawable = imageLoader.loadDrawable(Icon.createWithData(array, 0, array.size))
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validSystemResourceIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                Resources.getSystem().getDrawable(android.R.drawable.ic_dialog_alert, context.theme)
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    Icon.createWithResource("android", android.R.drawable.ic_dialog_alert)
+                )
+            assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
+        }
+
+    @Test
+    fun invalidDifferentPackageResourceIcon_returnsNull() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    Icon.createWithResource(
+                        "noooope.wrong.package",
+                        R.drawable.dessert_zombiegingerbread
+                    )
+                )
+            assertThat(loadedDrawable).isNull()
+        }
+
+    @Test
+    fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 160)
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(160)
+            assertThat(loadedBitmap.height).isEqualTo(106)
+        }
+
+    @Test
+    fun validBitmapResource_heightMoreRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 50)
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(74)
+            assertThat(loadedBitmap.height).isEqualTo(50)
+        }
+
+    @Test
+    fun validBitmapResource_onlyWidthRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.File(imgFile),
+                    maxWidth = 160,
+                    maxHeight = ImageLoader.DO_NOT_RESIZE
+                )
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(160)
+            assertThat(loadedBitmap.height).isEqualTo(106)
+        }
+
+    @Test
+    fun validBitmapResource_onlyHeightRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.Res(R.drawable.bubble_thumbnail),
+                    maxWidth = ImageLoader.DO_NOT_RESIZE,
+                    maxHeight = 120
+                )
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(123)
+            assertThat(loadedBitmap.height).isEqualTo(120)
+        }
+
+    @Test
+    fun validVectorDrawable_loadDrawable_successfullyLoaded() =
+        testScope.runTest {
+            val loadedDrawable = imageLoader.loadDrawable(ImageLoader.Res(R.drawable.ic_settings))
+            assertThat(loadedDrawable).isNotNull()
+            assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
+        }
+
+    @Test
+    fun validVectorDrawable_loadBitmap_returnsNull() =
+        testScope.runTest {
+            val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Res(R.drawable.ic_settings))
+            assertThat(loadedBitmap).isNull()
+        }
+
+    @Test
+    fun validVectorDrawableIcon_loadDrawable_successfullyLoaded() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(Icon.createWithResource(context, R.drawable.ic_settings))
+            assertThat(loadedDrawable).isNotNull()
+            assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
+        }
+
+    @Test
+    fun hardwareAllocator_returnsHardwareBitmap() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.File(imgFile),
+                    allocator = ImageDecoder.ALLOCATOR_HARDWARE
+                )
+            assertThat(loadedDrawable).isNotNull()
+            assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
+                .isEqualTo(Bitmap.Config.HARDWARE)
+        }
+
+    @Test
+    fun softwareAllocator_returnsSoftwareBitmap() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.File(imgFile),
+                    allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+                )
+            assertThat(loadedDrawable).isNotNull()
+            assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
+                .isNotEqualTo(Bitmap.Config.HARDWARE)
+        }
+
+    private fun assertBitmapInDrawable(drawable: Drawable?): Bitmap {
+        assertThat(drawable).isNotNull()
+        assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
+        return (drawable as BitmapDrawable).bitmap
+    }
+
+    private fun assertBitmapEqualToDrawable(actual: Drawable?, expected: Bitmap) {
+        val actualBitmap = assertBitmapInDrawable(actual)
+        assertBitmapEqualToBitmap(actualBitmap, expected)
+    }
+
+    private fun assertBitmapEqualToBitmap(actual: Bitmap?, expected: Bitmap) {
+        assertThat(actual).isNotNull()
+        assertThat(actual?.width).isEqualTo(expected.width)
+        assertThat(actual?.height).isEqualTo(expected.height)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index fb7d379..5d83f56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -157,11 +157,34 @@
             assertThat(strongBiometricAllowed()).isFalse()
         }
 
+    @Test
+    fun convenienceBiometricAllowedChange() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            val convenienceBiometricAllowed =
+                collectLastValue(underTest.isNonStrongBiometricAllowed)
+            runCurrent()
+
+            onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isTrue()
+
+            onNonStrongAuthChanged(false, ANOTHER_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isTrue()
+
+            onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isFalse()
+        }
+
     private fun onStrongAuthChanged(flags: Int, userId: Int) {
         strongAuthTracker.value.stub.onStrongAuthRequiredChanged(flags, userId)
         testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
     }
 
+    private fun onNonStrongAuthChanged(allowed: Boolean, userId: Int) {
+        strongAuthTracker.value.stub.onIsNonStrongBiometricAllowedChanged(allowed, userId)
+        testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+    }
+
     @Test
     fun fingerprintDisabledByDpmChange() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
new file mode 100644
index 0000000..6e002f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+import android.app.StatusBarManager.SESSION_KEYGUARD
+import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_PRIMARY
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
+import android.hardware.biometrics.ComponentInfoInternal
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.os.CancellationSignal
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId.fakeInstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.FakeKeyguardStateController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.SystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
+    private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl
+
+    @Mock private lateinit var faceManager: FaceManager
+    @Mock private lateinit var bypassController: KeyguardBypassController
+    @Mock private lateinit var sessionTracker: SessionTracker
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var dumpManager: DumpManager
+
+    @Captor
+    private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
+
+    @Captor
+    private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
+    @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
+
+    private lateinit var bypassStateChangedListener:
+        KotlinArgumentCaptor<KeyguardBypassController.OnBypassStateChangedListener>
+
+    @Captor
+    private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
+    private lateinit var testDispatcher: TestDispatcher
+
+    private lateinit var testScope: TestScope
+    private lateinit var fakeUserRepository: FakeUserRepository
+    private lateinit var authStatus: FlowValue<AuthenticationStatus?>
+    private lateinit var detectStatus: FlowValue<DetectionStatus?>
+    private lateinit var authRunning: FlowValue<Boolean?>
+    private lateinit var lockedOut: FlowValue<Boolean?>
+    private lateinit var canFaceAuthRun: FlowValue<Boolean?>
+    private lateinit var authenticated: FlowValue<Boolean?>
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var deviceEntryFingerprintAuthRepository:
+        FakeDeviceEntryFingerprintAuthRepository
+    private lateinit var trustRepository: FakeTrustRepository
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var keyguardInteractor: KeyguardInteractor
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+    private lateinit var fakeCommandQueue: FakeCommandQueue
+    private lateinit var featureFlags: FakeFeatureFlags
+
+    private var wasAuthCancelled = false
+    private var wasDetectCancelled = false
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        fakeUserRepository = FakeUserRepository()
+        fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
+        testDispatcher = StandardTestDispatcher()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+        trustRepository = FakeTrustRepository()
+        keyguardRepository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
+        fakeCommandQueue = FakeCommandQueue()
+        keyguardInteractor =
+            KeyguardInteractor(
+                keyguardRepository,
+                fakeCommandQueue,
+                featureFlags,
+                bouncerRepository
+            )
+        alternateBouncerInteractor =
+            AlternateBouncerInteractor(
+                bouncerRepository = bouncerRepository,
+                biometricSettingsRepository = biometricSettingsRepository,
+                deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+                systemClock = mock(SystemClock::class.java),
+                keyguardStateController = FakeKeyguardStateController(),
+                statusBarStateController = mock(StatusBarStateController::class.java),
+            )
+
+        bypassStateChangedListener =
+            KotlinArgumentCaptor(KeyguardBypassController.OnBypassStateChangedListener::class.java)
+        testScope = TestScope(testDispatcher)
+        whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
+        whenever(faceManager.sensorPropertiesInternal)
+            .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+        whenever(bypassController.bypassEnabled).thenReturn(true)
+        underTest = createDeviceEntryFaceAuthRepositoryImpl(faceManager, bypassController)
+    }
+
+    private fun createDeviceEntryFaceAuthRepositoryImpl(
+        fmOverride: FaceManager? = faceManager,
+        bypassControllerOverride: KeyguardBypassController? = bypassController
+    ) =
+        DeviceEntryFaceAuthRepositoryImpl(
+            mContext,
+            fmOverride,
+            fakeUserRepository,
+            bypassControllerOverride,
+            testScope.backgroundScope,
+            testDispatcher,
+            sessionTracker,
+            uiEventLogger,
+            FaceAuthenticationLogger(logcatLogBuffer("DeviceEntryFaceAuthRepositoryLog")),
+            biometricSettingsRepository,
+            deviceEntryFingerprintAuthRepository,
+            trustRepository,
+            keyguardRepository,
+            keyguardInteractor,
+            alternateBouncerInteractor,
+            dumpManager,
+        )
+
+    @Test
+    fun faceAuthRunsAndProvidesAuthStatusUpdates() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+            uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+            assertThat(authRunning()).isTrue()
+
+            val successResult = successResult()
+            authenticationCallback.value.onAuthenticationSucceeded(successResult)
+
+            assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+            assertThat(authenticated()).isTrue()
+            assertThat(authRunning()).isFalse()
+        }
+
+    private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
+        verify(uiEventLogger)
+            .logWithInstanceIdAndPosition(
+                faceAuthUiEvent,
+                0,
+                null,
+                keyguardSessionId,
+                faceAuthUiEvent.extraInfo
+            )
+    }
+
+    @Test
+    fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
+        testScope.runTest {
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+            clearInvocations(faceManager)
+            clearInvocations(uiEventLogger)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            verifyNoMoreInteractions(faceManager)
+            verifyNoMoreInteractions(uiEventLogger)
+        }
+
+    @Test
+    fun faceLockoutStatusIsPropagated() =
+        testScope.runTest {
+            initCollectors()
+            verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_LOCKOUT_PERMANENT,
+                "face locked out"
+            )
+
+            assertThat(lockedOut()).isTrue()
+
+            faceLockoutResetCallback.value.onLockoutReset(0)
+            assertThat(lockedOut()).isFalse()
+        }
+
+    @Test
+    fun faceDetectionSupportIsTheCorrectValue() =
+        testScope.runTest {
+            assertThat(
+                    createDeviceEntryFaceAuthRepositoryImpl(fmOverride = null).isDetectionSupported
+                )
+                .isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(
+                    listOf(
+                        createFaceSensorProperties(supportsFaceDetection = false),
+                        createFaceSensorProperties(supportsFaceDetection = true)
+                    )
+                )
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(
+                    listOf(
+                        createFaceSensorProperties(supportsFaceDetection = true),
+                        createFaceSensorProperties(supportsFaceDetection = false)
+                    )
+                )
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isTrue()
+        }
+
+    @Test
+    fun cancelStopsFaceAuthentication() =
+        testScope.runTest {
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            var wasAuthCancelled = false
+            cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+
+            underTest.cancel()
+            assertThat(wasAuthCancelled).isTrue()
+            assertThat(authRunning()).isFalse()
+        }
+
+    @Test
+    fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
+
+    @Test
+    fun faceDetectionRunsAndPropagatesDetectionStatus() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+
+            underTest.detect()
+            faceDetectIsCalled()
+
+            detectionCallback.value.onFaceDetected(1, 1, true)
+
+            assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+        }
+
+    @Test
+    fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+            clearInvocations(faceManager)
+
+            underTest.detect()
+
+            verify(faceManager, never())
+                .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
+        }
+
+    @Test
+    fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
+        testScope.runTest {
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            // Enter cancelling state
+            underTest.cancel()
+            clearInvocations(faceManager)
+
+            // Auth is while cancelling.
+            underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+            // Auth is not started
+            verifyNoMoreInteractions(faceManager)
+
+            // Auth is done cancelling.
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_CANCELED,
+                "First auth attempt cancellation completed"
+            )
+            assertThat(authStatus())
+                .isEqualTo(
+                    ErrorAuthenticationStatus(
+                        FACE_ERROR_CANCELED,
+                        "First auth attempt cancellation completed"
+                    )
+                )
+
+            faceAuthenticateIsCalled()
+            uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+        }
+
+    @Test
+    fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            clearInvocations(faceManager)
+            underTest.cancel()
+            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+        }
+
+    @Test
+    fun faceHelpMessagesAreIgnoredBasedOnConfig() =
+        testScope.runTest {
+            overrideResource(
+                R.array.config_face_acquire_device_entry_ignorelist,
+                intArrayOf(10, 11)
+            )
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            authenticationCallback.value.onAuthenticationHelp(9, "help msg")
+            authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
+            authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
+
+            assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+        }
+
+    @Test
+    fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
+        testScope.runTest {
+            fakeUserRepository.setSelectedUserInfo(primaryUser)
+            underTest.dump(PrintWriter(StringWriter()), emptyArray())
+
+            underTest =
+                createDeviceEntryFaceAuthRepositoryImpl(
+                    fmOverride = null,
+                    bypassControllerOverride = null
+                )
+            fakeUserRepository.setSelectedUserInfo(primaryUser)
+
+            underTest.dump(PrintWriter(StringWriter()), emptyArray())
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfFaceIsNotEnrolled() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { biometricSettingsRepository.setFaceEnrolled(false) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfFaceIsNotEnabled() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { biometricSettingsRepository.setIsFaceAuthEnabled(false) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfUserIsInLockdown() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { biometricSettingsRepository.setIsUserInLockdown(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfUserIsCurrentlySwitching() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { fakeUserRepository.setUserSwitching(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenFpIsLockedOut() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { deviceEntryFingerprintAuthRepository.setLockedOut(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenDeviceIsGoingToSleep() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                keyguardRepository.setWakefulnessModel(
+                    WakefulnessModel(
+                        state = WakefulnessState.STARTING_TO_SLEEP,
+                        isWakingUpOrAwake = false,
+                        lastWakeReason = WakeSleepReason.OTHER,
+                        lastSleepReason = WakeSleepReason.OTHER,
+                    )
+                )
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenDeviceIsSleeping() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                keyguardRepository.setWakefulnessModel(
+                    WakefulnessModel(
+                        state = WakefulnessState.ASLEEP,
+                        isWakingUpOrAwake = false,
+                        lastWakeReason = WakeSleepReason.OTHER,
+                        lastSleepReason = WakeSleepReason.OTHER,
+                    )
+                )
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenNonStrongBiometricIsNotAllowed() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenCurrentUserIsNotPrimary() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                launch { fakeUserRepository.setSelectedUserInfo(secondaryUser) }
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenSecureCameraIsActive() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                bouncerRepository.setAlternateVisible(false)
+                fakeCommandQueue.doForEachCallback {
+                    it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+                }
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunOnUnsupportedPosture() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(false)
+            }
+        }
+
+    @Test
+    fun authenticateFallbacksToDetectionWhenItCannotRun() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+            whenever(bypassController.bypassEnabled).thenReturn(true)
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            // Flip one precondition to false.
+            biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+            assertThat(canFaceAuthRun()).isFalse()
+            underTest.authenticate(
+                FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+                fallbackToDetection = true
+            )
+            faceAuthenticateIsNotCalled()
+
+            faceDetectIsCalled()
+        }
+
+    @Test
+    fun everythingWorksWithFaceAuthRefactorFlagDisabled() =
+        testScope.runTest {
+            featureFlags.set(FACE_AUTH_REFACTOR, false)
+
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+
+            // Collecting any flows exposed in the public API doesn't throw any error
+            authStatus()
+            detectStatus()
+            authRunning()
+            lockedOut()
+            canFaceAuthRun()
+            authenticated()
+        }
+
+    @Test
+    fun isAuthenticatedIsFalseWhenFaceAuthFails() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationFailed()
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsFalseWhenFaceAuthErrorsOut() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationError(-1, "some error")
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            keyguardRepository.setKeyguardGoingAway(true)
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            fakeUserRepository.setUserSwitching(true)
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun detectDoesNotRunWhenFaceIsNotEnrolled() =
+        testScope.runTest {
+            testGatingCheckForDetect { biometricSettingsRepository.setFaceEnrolled(false) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenFaceIsNotEnabled() =
+        testScope.runTest {
+            testGatingCheckForDetect { biometricSettingsRepository.setIsFaceAuthEnabled(false) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenUserSwitchingInProgress() =
+        testScope.runTest { testGatingCheckForDetect { fakeUserRepository.setUserSwitching(true) } }
+
+    @Test
+    fun detectDoesNotRunWhenKeyguardGoingAway() =
+        testScope.runTest {
+            testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenDeviceSleepingStartingToSleep() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                keyguardRepository.setWakefulnessModel(
+                    WakefulnessModel(
+                        state = WakefulnessState.STARTING_TO_SLEEP,
+                        isWakingUpOrAwake = false,
+                        lastWakeReason = WakeSleepReason.OTHER,
+                        lastSleepReason = WakeSleepReason.OTHER,
+                    )
+                )
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenSecureCameraIsActive() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                bouncerRepository.setAlternateVisible(false)
+                fakeCommandQueue.doForEachCallback {
+                    it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+                }
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenFaceAuthNotSupportedInCurrentPosture() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(false)
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenCurrentUserInLockdown() =
+        testScope.runTest {
+            testGatingCheckForDetect { biometricSettingsRepository.setIsUserInLockdown(true) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenBypassIsNotEnabled() =
+        testScope.runTest {
+            runCurrent()
+            verify(bypassController)
+                .registerOnBypassStateChangedListener(bypassStateChangedListener.capture())
+
+            testGatingCheckForDetect {
+                bypassStateChangedListener.value.onBypassStateChanged(false)
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenNonStrongBiometricIsAllowed() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunIfUdfpsIsRunning() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                deviceEntryFingerprintAuthRepository.setAvailableFpSensorType(
+                    BiometricType.UNDER_DISPLAY_FINGERPRINT
+                )
+                deviceEntryFingerprintAuthRepository.setIsRunning(true)
+            }
+        }
+
+    private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
+        initCollectors()
+        allPreconditionsToRunFaceAuthAreTrue()
+
+        gatingCheckModifier()
+        runCurrent()
+
+        // gating check doesn't allow face auth to run.
+        assertThat(underTest.canRunFaceAuth.value).isFalse()
+
+        // flip the gating check back on.
+        allPreconditionsToRunFaceAuthAreTrue()
+
+        triggerFaceAuth(false)
+
+        // Flip gating check off
+        gatingCheckModifier()
+        runCurrent()
+
+        // Stops currently running auth
+        assertThat(wasAuthCancelled).isTrue()
+        clearInvocations(faceManager)
+
+        // Try auth again
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+        // Auth can't run again
+        faceAuthenticateIsNotCalled()
+    }
+
+    private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+        initCollectors()
+        allPreconditionsToRunFaceAuthAreTrue()
+
+        // This will stop face auth from running but is required to be false for detect.
+        biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+        runCurrent()
+
+        assertThat(canFaceAuthRun()).isFalse()
+
+        // Trigger authenticate with detection fallback
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+
+        faceAuthenticateIsNotCalled()
+        faceDetectIsCalled()
+        cancellationSignal.value.setOnCancelListener { wasDetectCancelled = true }
+
+        // Flip gating check
+        gatingCheckModifier()
+        runCurrent()
+
+        // Stops currently running detect
+        assertThat(wasDetectCancelled).isTrue()
+        clearInvocations(faceManager)
+
+        // Try to run detect again
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+
+        // Detect won't run because preconditions are not true anymore.
+        faceDetectIsNotCalled()
+    }
+
+    private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+        assertThat(canFaceAuthRun()).isTrue()
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+        faceAuthenticateIsCalled()
+        assertThat(authRunning()).isTrue()
+        cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+    }
+
+    private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
+        biometricSettingsRepository.setFaceEnrolled(true)
+        biometricSettingsRepository.setIsFaceAuthEnabled(true)
+        fakeUserRepository.setUserSwitching(false)
+        deviceEntryFingerprintAuthRepository.setLockedOut(false)
+        trustRepository.setCurrentUserTrusted(false)
+        keyguardRepository.setKeyguardGoingAway(false)
+        keyguardRepository.setWakefulnessModel(
+            WakefulnessModel(
+                WakefulnessState.STARTING_TO_WAKE,
+                true,
+                WakeSleepReason.OTHER,
+                WakeSleepReason.OTHER
+            )
+        )
+        biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+        biometricSettingsRepository.setIsUserInLockdown(false)
+        fakeUserRepository.setSelectedUserInfo(primaryUser)
+        biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
+        bouncerRepository.setAlternateVisible(true)
+        runCurrent()
+    }
+
+    private suspend fun TestScope.initCollectors() {
+        authStatus = collectLastValue(underTest.authenticationStatus)
+        detectStatus = collectLastValue(underTest.detectionStatus)
+        authRunning = collectLastValue(underTest.isAuthRunning)
+        lockedOut = collectLastValue(underTest.isLockedOut)
+        canFaceAuthRun = collectLastValue(underTest.canRunFaceAuth)
+        authenticated = collectLastValue(underTest.isAuthenticated)
+        fakeUserRepository.setSelectedUserInfo(primaryUser)
+    }
+
+    private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
+
+    private fun faceDetectIsCalled() {
+        verify(faceManager)
+            .detectFace(
+                cancellationSignal.capture(),
+                detectionCallback.capture(),
+                eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+            )
+    }
+
+    private fun faceAuthenticateIsCalled() {
+        verify(faceManager)
+            .authenticate(
+                isNull(),
+                cancellationSignal.capture(),
+                authenticationCallback.capture(),
+                isNull(),
+                eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+            )
+    }
+
+    private fun faceAuthenticateIsNotCalled() {
+        verify(faceManager, never())
+            .authenticate(
+                isNull(),
+                any(),
+                any(),
+                isNull(),
+                any(FaceAuthenticateOptions::class.java)
+            )
+    }
+
+    private fun faceDetectIsNotCalled() {
+        verify(faceManager, never())
+            .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
+    }
+
+    private fun createFaceSensorProperties(
+        supportsFaceDetection: Boolean
+    ): FaceSensorPropertiesInternal {
+        val componentInfo =
+            listOf(
+                ComponentInfoInternal(
+                    "faceSensor" /* componentId */,
+                    "vendor/model/revision" /* hardwareVersion */,
+                    "1.01" /* firmwareVersion */,
+                    "00000001" /* serialNumber */,
+                    "" /* softwareVersion */
+                )
+            )
+        return FaceSensorPropertiesInternal(
+            0 /* id */,
+            FaceSensorProperties.STRENGTH_STRONG,
+            1 /* maxTemplatesAllowed */,
+            componentInfo,
+            FaceSensorProperties.TYPE_UNKNOWN,
+            supportsFaceDetection /* supportsFaceDetection */,
+            true /* supportsSelfIllumination */,
+            false /* resetLockoutRequiresChallenge */
+        )
+    }
+
+    companion object {
+        const val primaryUserId = 1
+        val keyguardSessionId = fakeInstanceId(10)!!
+        val primaryUser = UserInfo(primaryUserId, "test user", FLAG_PRIMARY)
+
+        val secondaryUser = UserInfo(2, "secondary user", 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 70f766f..e57b044 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
 import android.hardware.biometrics.BiometricSourceType
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +31,6 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,11 +70,6 @@
             )
     }
 
-    @After
-    fun tearDown() {
-        //        verify(keyguardUpdateMonitor).removeCallback(updateMonitorCallback.value)
-    }
-
     @Test
     fun isLockedOut_whenFingerprintLockoutStateChanges_emitsNewValue() =
         testScope.runTest {
@@ -129,29 +124,55 @@
     }
 
     @Test
-    fun enabledFingerprintTypeProvidesTheCorrectOutput() =
+    fun enabledFingerprintTypeProvidesTheCorrectOutputForSpfs() =
         testScope.runTest {
             whenever(authController.isSfpsSupported).thenReturn(true)
             whenever(authController.isUdfpsSupported).thenReturn(false)
             whenever(authController.isRearFpsSupported).thenReturn(false)
 
-            assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.SIDE_FINGERPRINT)
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.SIDE_FINGERPRINT)
+        }
 
+    @Test
+    fun enabledFingerprintTypeProvidesTheCorrectOutputForUdfps() =
+        testScope.runTest {
             whenever(authController.isSfpsSupported).thenReturn(false)
             whenever(authController.isUdfpsSupported).thenReturn(true)
             whenever(authController.isRearFpsSupported).thenReturn(false)
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
+        }
 
-            assertThat(underTest.availableFpSensorType)
-                .isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
-
+    @Test
+    fun enabledFingerprintTypeProvidesTheCorrectOutputForRearFps() =
+        testScope.runTest {
             whenever(authController.isSfpsSupported).thenReturn(false)
             whenever(authController.isUdfpsSupported).thenReturn(false)
             whenever(authController.isRearFpsSupported).thenReturn(true)
 
-            assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.REAR_FINGERPRINT)
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
 
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.REAR_FINGERPRINT)
+        }
+
+    @Test
+    fun enabledFingerprintTypeProvidesTheCorrectOutputAfterAllAuthenticatorsAreRegistered() =
+        testScope.runTest {
+            whenever(authController.isSfpsSupported).thenReturn(false)
+            whenever(authController.isUdfpsSupported).thenReturn(false)
             whenever(authController.isRearFpsSupported).thenReturn(false)
+            whenever(authController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(false)
 
-            assertThat(underTest.availableFpSensorType).isNull()
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+            runCurrent()
+
+            val callback = ArgumentCaptor.forClass(AuthController.Callback::class.java)
+            verify(authController).addCallback(callback.capture())
+            assertThat(availableFpSensorType()).isNull()
+
+            whenever(authController.isUdfpsSupported).thenReturn(true)
+            callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
deleted file mode 100644
index d55370b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.app.StatusBarManager.SESSION_KEYGUARD
-import android.content.pm.UserInfo
-import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
-import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
-import android.hardware.biometrics.ComponentInfoInternal
-import android.hardware.face.FaceAuthenticateOptions
-import android.hardware.face.FaceManager
-import android.hardware.face.FaceSensorProperties
-import android.hardware.face.FaceSensorPropertiesInternal
-import android.os.CancellationSignal
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.InstanceId.fakeInstanceId
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.FlowValue
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
-import com.android.systemui.log.FaceAuthenticationLogger
-import com.android.systemui.log.SessionTracker
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.isNull
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardFaceAuthManagerTest : SysuiTestCase() {
-    private lateinit var underTest: KeyguardFaceAuthManagerImpl
-
-    @Mock private lateinit var faceManager: FaceManager
-    @Mock private lateinit var bypassController: KeyguardBypassController
-    @Mock private lateinit var sessionTracker: SessionTracker
-    @Mock private lateinit var uiEventLogger: UiEventLogger
-    @Mock private lateinit var dumpManager: DumpManager
-
-    @Captor
-    private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
-    @Captor
-    private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
-    @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
-    @Captor
-    private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
-    private lateinit var testDispatcher: TestDispatcher
-
-    private lateinit var testScope: TestScope
-    private lateinit var fakeUserRepository: FakeUserRepository
-    private lateinit var authStatus: FlowValue<AuthenticationStatus?>
-    private lateinit var detectStatus: FlowValue<DetectionStatus?>
-    private lateinit var authRunning: FlowValue<Boolean?>
-    private lateinit var lockedOut: FlowValue<Boolean?>
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        fakeUserRepository = FakeUserRepository()
-        fakeUserRepository.setUserInfos(listOf(currentUser))
-        testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
-        whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
-        whenever(bypassController.bypassEnabled).thenReturn(true)
-        underTest = createFaceAuthManagerImpl(faceManager)
-    }
-
-    private fun createFaceAuthManagerImpl(
-        fmOverride: FaceManager? = faceManager,
-        bypassControllerOverride: KeyguardBypassController? = bypassController
-    ) =
-        KeyguardFaceAuthManagerImpl(
-            mContext,
-            fmOverride,
-            fakeUserRepository,
-            bypassControllerOverride,
-            testScope.backgroundScope,
-            testDispatcher,
-            sessionTracker,
-            uiEventLogger,
-            FaceAuthenticationLogger(logcatLogBuffer("KeyguardFaceAuthManagerLog")),
-            dumpManager,
-        )
-
-    @Test
-    fun faceAuthRunsAndProvidesAuthStatusUpdates() =
-        testScope.runTest {
-            testSetup(this)
-
-            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-            uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-
-            assertThat(authRunning()).isTrue()
-
-            val successResult = successResult()
-            authenticationCallback.value.onAuthenticationSucceeded(successResult)
-
-            assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
-
-            assertThat(authRunning()).isFalse()
-        }
-
-    private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
-        verify(uiEventLogger)
-            .logWithInstanceIdAndPosition(
-                faceAuthUiEvent,
-                0,
-                null,
-                keyguardSessionId,
-                faceAuthUiEvent.extraInfo
-            )
-    }
-
-    @Test
-    fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-            clearInvocations(faceManager)
-            clearInvocations(uiEventLogger)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            verifyNoMoreInteractions(faceManager)
-            verifyNoMoreInteractions(uiEventLogger)
-        }
-
-    @Test
-    fun faceLockoutStatusIsPropagated() =
-        testScope.runTest {
-            testSetup(this)
-            verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            authenticationCallback.value.onAuthenticationError(
-                FACE_ERROR_LOCKOUT_PERMANENT,
-                "face locked out"
-            )
-
-            assertThat(lockedOut()).isTrue()
-
-            faceLockoutResetCallback.value.onLockoutReset(0)
-            assertThat(lockedOut()).isFalse()
-        }
-
-    @Test
-    fun faceDetectionSupportIsTheCorrectValue() =
-        testScope.runTest {
-            assertThat(createFaceAuthManagerImpl(fmOverride = null).isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(
-                    listOf(
-                        createFaceSensorProperties(supportsFaceDetection = false),
-                        createFaceSensorProperties(supportsFaceDetection = true)
-                    )
-                )
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(
-                    listOf(
-                        createFaceSensorProperties(supportsFaceDetection = true),
-                        createFaceSensorProperties(supportsFaceDetection = false)
-                    )
-                )
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isTrue()
-        }
-
-    @Test
-    fun cancelStopsFaceAuthentication() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            var wasAuthCancelled = false
-            cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
-
-            underTest.cancel()
-            assertThat(wasAuthCancelled).isTrue()
-            assertThat(authRunning()).isFalse()
-        }
-
-    @Test
-    fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
-
-    @Test
-    fun faceDetectionRunsAndPropagatesDetectionStatus() =
-        testScope.runTest {
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
-            underTest = createFaceAuthManagerImpl()
-            testSetup(this)
-
-            underTest.detect()
-            faceDetectIsCalled()
-
-            detectionCallback.value.onFaceDetected(1, 1, true)
-
-            assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
-        }
-
-    @Test
-    fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
-        testScope.runTest {
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
-            underTest = createFaceAuthManagerImpl()
-            testSetup(this)
-            clearInvocations(faceManager)
-
-            underTest.detect()
-
-            verify(faceManager, never()).detectFace(any(), any(), any())
-        }
-
-    @Test
-    fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            // Enter cancelling state
-            underTest.cancel()
-            clearInvocations(faceManager)
-
-            // Auth is while cancelling.
-            underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
-            // Auth is not started
-            verifyNoMoreInteractions(faceManager)
-
-            // Auth is done cancelling.
-            authenticationCallback.value.onAuthenticationError(
-                FACE_ERROR_CANCELED,
-                "First auth attempt cancellation completed"
-            )
-            assertThat(authStatus())
-                .isEqualTo(
-                    ErrorAuthenticationStatus(
-                        FACE_ERROR_CANCELED,
-                        "First auth attempt cancellation completed"
-                    )
-                )
-
-            faceAuthenticateIsCalled()
-            uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
-        }
-
-    @Test
-    fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            clearInvocations(faceManager)
-            underTest.cancel()
-            advanceTimeBy(KeyguardFaceAuthManagerImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-        }
-
-    @Test
-    fun faceHelpMessagesAreIgnoredBasedOnConfig() =
-        testScope.runTest {
-            overrideResource(
-                R.array.config_face_acquire_device_entry_ignorelist,
-                intArrayOf(10, 11)
-            )
-            underTest = createFaceAuthManagerImpl()
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            authenticationCallback.value.onAuthenticationHelp(9, "help msg")
-            authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
-            authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
-
-            assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
-        }
-
-    @Test
-    fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
-        testScope.runTest {
-            fakeUserRepository.setSelectedUserInfo(currentUser)
-            underTest.dump(PrintWriter(StringWriter()), emptyArray())
-
-            underTest =
-                createFaceAuthManagerImpl(fmOverride = null, bypassControllerOverride = null)
-            fakeUserRepository.setSelectedUserInfo(currentUser)
-
-            underTest.dump(PrintWriter(StringWriter()), emptyArray())
-        }
-
-    private suspend fun testSetup(testScope: TestScope) {
-        with(testScope) {
-            authStatus = collectLastValue(underTest.authenticationStatus)
-            detectStatus = collectLastValue(underTest.detectionStatus)
-            authRunning = collectLastValue(underTest.isAuthRunning)
-            lockedOut = collectLastValue(underTest.isLockedOut)
-            fakeUserRepository.setSelectedUserInfo(currentUser)
-        }
-    }
-
-    private fun successResult() = FaceManager.AuthenticationResult(null, null, currentUserId, false)
-
-    private fun faceDetectIsCalled() {
-        verify(faceManager)
-            .detectFace(
-                cancellationSignal.capture(),
-                detectionCallback.capture(),
-                eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build())
-            )
-    }
-
-    private fun faceAuthenticateIsCalled() {
-        verify(faceManager)
-            .authenticate(
-                isNull(),
-                cancellationSignal.capture(),
-                authenticationCallback.capture(),
-                isNull(),
-                eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build())
-            )
-    }
-
-    private fun createFaceSensorProperties(
-        supportsFaceDetection: Boolean
-    ): FaceSensorPropertiesInternal {
-        val componentInfo =
-            listOf(
-                ComponentInfoInternal(
-                    "faceSensor" /* componentId */,
-                    "vendor/model/revision" /* hardwareVersion */,
-                    "1.01" /* firmwareVersion */,
-                    "00000001" /* serialNumber */,
-                    "" /* softwareVersion */
-                )
-            )
-        return FaceSensorPropertiesInternal(
-            0 /* id */,
-            FaceSensorProperties.STRENGTH_STRONG,
-            1 /* maxTemplatesAllowed */,
-            componentInfo,
-            FaceSensorProperties.TYPE_UNKNOWN,
-            supportsFaceDetection /* supportsFaceDetection */,
-            true /* supportsSelfIllumination */,
-            false /* resetLockoutRequiresChallenge */
-        )
-    }
-
-    companion object {
-        const val currentUserId = 1
-        val keyguardSessionId = fakeInstanceId(10)!!
-        val currentUser = UserInfo(currentUserId, "test user", 0)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 7f30162..68d694a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,18 +18,16 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager
-import android.content.Context
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
-import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.CommandQueue
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.test.TestScope
@@ -38,7 +36,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -56,7 +53,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
-        commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
+        commandQueue = FakeCommandQueue()
         testScope = TestScope()
         repository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
@@ -174,22 +171,3 @@
             assertThat(secureCameraActive()).isFalse()
         }
 }
-
-class FakeCommandQueue(val context: Context, val displayTracker: DisplayTracker) :
-    CommandQueue(context, displayTracker) {
-    private val callbacks = mutableListOf<Callbacks>()
-
-    override fun addCallback(callback: Callbacks) {
-        callbacks.add(callback)
-    }
-
-    override fun removeCallback(callback: Callbacks) {
-        callbacks.remove(callback)
-    }
-
-    fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
-        callbacks.forEach { func(it) }
-    }
-
-    fun callbackCount(): Int = callbacks.size
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
index aa54a1c..447b333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.ArgumentMatchers.any;
 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;
@@ -37,6 +38,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -64,6 +66,8 @@
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private UiEventLogger mUiEventLogger;
 
     @Captor
     ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -87,7 +91,8 @@
                 mStatusBarService,
                 mAuthController,
                 mKeyguardUpdateMonitor,
-                mKeyguardStateController
+                mKeyguardStateController,
+                mUiEventLogger
         );
     }
 
@@ -238,6 +243,62 @@
                 eq(SESSION_KEYGUARD), any(InstanceId.class));
     }
 
+    @Test
+    public void uiEventLoggedOnEndSessionWhenDeviceStartsSleeping() throws RemoteException {
+        // GIVEN session tracker start
+        mSessionTracker.start();
+        captureKeyguardUpdateMonitorCallback();
+        captureKeyguardStateControllerCallback();
+
+        // GIVEN keyguard becomes visible (ie: from lockdown), so there's a valid keyguard
+        // session running
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // WHEN device starts going to sleep
+        mKeyguardUpdateMonitorCallback.onStartedGoingToSleep(0);
+
+        // THEN UI event is logged
+        verify(mUiEventLogger).log(
+                eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP),
+                any(InstanceId.class));
+    }
+
+    @Test
+    public void noUiEventLoggedOnEndSessionWhenDeviceStartsSleepingWithoutStartSession()
+            throws RemoteException {
+        // GIVEN session tracker start without any valid sessions
+        mSessionTracker.start();
+        captureKeyguardUpdateMonitorCallback();
+
+        // WHEN device starts going to sleep when there was no started sessions
+        mKeyguardUpdateMonitorCallback.onStartedGoingToSleep(0);
+
+        // THEN UI event is never logged
+        verify(mUiEventLogger, never()).log(
+                eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP),
+                any(InstanceId.class));
+    }
+
+    @Test
+    public void uiEventLoggedOnEndSessionWhenKeyguardGoingAway() throws RemoteException {
+        // GIVEN session tracker started w/o any sessions
+        mSessionTracker.start();
+        captureKeyguardUpdateMonitorCallback();
+        captureKeyguardStateControllerCallback();
+
+        // WHEN keyguard was showing and now it's not
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // THEN UI event is logged
+        verify(mUiEventLogger).log(
+                eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY),
+                any(InstanceId.class));
+    }
+
     void captureKeyguardUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(
                 mKeyguardUpdateMonitorCallbackCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index fbe089a..ba29ca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -369,6 +369,39 @@
 
         verifyZeroInteractions(context, bubbles, eventLogger)
     }
+
+    @Test
+    fun showNoteTask_keyboardShortcut_shouldStartActivity() {
+        val expectedInfo =
+                NOTE_TASK_INFO.copy(
+                        entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
+                        isKeyguardLocked = true,
+                )
+        whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
+        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+
+        createNoteTaskController()
+                .showNoteTask(
+                        entryPoint = expectedInfo.entryPoint!!,
+                )
+
+        val intentCaptor = argumentCaptor<Intent>()
+        val userCaptor = argumentCaptor<UserHandle>()
+        verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+        intentCaptor.value.let { intent ->
+            assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
+            assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
+                    .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
+                    .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
+            assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, true)).isFalse()
+        }
+        assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
+        verify(eventLogger).logNoteTaskOpened(expectedInfo)
+        verifyZeroInteractions(bubbles)
+    }
     // endregion
 
     // region setNoteTaskShortcutEnabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index cd67e8d..ec4daee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -98,14 +98,24 @@
     // region handleSystemKey
     @Test
     fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
-        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)
+        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
 
         verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
     }
 
     @Test
+    fun handleSystemKey_receiveKeyboardShortcut_shouldShowNoteTask() {
+        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_N, 0, KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON))
+
+        verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
+    }
+    
+    @Test
     fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
-        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
+        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_UNKNOWN))
 
         verifyZeroInteractions(controller)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 6f54f62..f5a3bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -111,8 +111,6 @@
         MockitoAnnotations.initMocks(this);
 
         mDeviceConfigProxyFake = new DeviceConfigProxyFake();
-        mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, "true", false);
         mSystemClock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(mSystemClock);
         mBackgroundExecutor = new FakeExecutor(mSystemClock);
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 46af89e..9ca7a85 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
@@ -40,6 +40,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.settings.UserTracker;
 
 import org.junit.After;
@@ -64,6 +65,8 @@
     private QSHost mQSHost;
     @Mock
     private Context mMockContext;
+    @Mock
+    private CustomTileAddedRepository mCustomTileAddedRepository;
 
     private HandlerThread mThread;
     private Handler mHandler;
@@ -86,8 +89,9 @@
 
         mComponentName = new ComponentName(mContext, TileServiceManagerTest.class);
         when(mTileLifecycle.getComponent()).thenReturn(mComponentName);
+
         mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mUserTracker,
-                mTileLifecycle);
+                mCustomTileAddedRepository, mTileLifecycle);
     }
 
     @After
@@ -98,28 +102,34 @@
 
     @Test
     public void testSetTileAddedIfNotAdded() {
-        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+        when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+                .thenReturn(false);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true);
+        verify(mCustomTileAddedRepository)
+                .setTileAdded(mComponentName, mUserTracker.getUserId(), true);
     }
 
     @Test
     public void testNotSetTileAddedIfAdded() {
-        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true);
+        when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+                .thenReturn(true);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true));
+        verify(mCustomTileAddedRepository, never())
+                .setTileAdded(eq(mComponentName), anyInt(), eq(true));
     }
 
     @Test
     public void testSetTileAddedCorrectUser() {
         int user = 10;
         when(mUserTracker.getUserId()).thenReturn(user);
-        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+        when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+                .thenReturn(false);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSHost).setTileAdded(mComponentName, user, true);
+        verify(mCustomTileAddedRepository)
+                .setTileAdded(mComponentName, user, true);
     }
 
     @Test
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 fb93367..12b5656 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
@@ -42,6 +42,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
@@ -98,6 +99,8 @@
     private PanelInteractor mPanelInteractor;
     @Captor
     private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksArgumentCaptor;
+    @Mock
+    private CustomTileAddedRepository mCustomTileAddedRepository;
 
     @Before
     public void setUp() throws Exception {
@@ -115,7 +118,7 @@
 
         mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher,
                 mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController,
-                mPanelInteractor);
+                mPanelInteractor, mCustomTileAddedRepository);
     }
 
     @After
@@ -293,9 +296,11 @@
         TestTileServices(QSHost host, Provider<Handler> handlerProvider,
                 BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
                 KeyguardStateController keyguardStateController, CommandQueue commandQueue,
-                StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) {
+                StatusBarIconController statusBarIconController, PanelInteractor panelInteractor,
+                CustomTileAddedRepository customTileAddedRepository) {
             super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
-                    commandQueue, statusBarIconController, panelInteractor);
+                    commandQueue, statusBarIconController, panelInteractor,
+                    customTileAddedRepository);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 0b9fbd9..59f0d96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -259,7 +259,6 @@
         val securityController = FakeSecurityController()
         val fgsManagerController =
             FakeFgsManagerController(
-                isAvailable = true,
                 showFooterDot = false,
                 numRunningPackages = 0,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
new file mode 100644
index 0000000..d7ab903
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.ComponentName
+import android.content.SharedPreferences
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.util.FakeSharedPreferences
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() {
+
+    private lateinit var underTest: CustomTileAddedSharedPrefsRepository
+
+    @Test
+    fun setTileAdded_inSharedPreferences() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        underTest.setTileAdded(TEST_COMPONENT, userId, added = true)
+        assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue()
+
+        underTest.setTileAdded(TEST_COMPONENT, userId, added = false)
+        assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isFalse()
+    }
+
+    @Test
+    fun setTileAdded_differentComponents() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        underTest.setTileAdded(TEST_COMPONENT, userId, added = true)
+
+        assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue()
+        assertThat(sharedPrefs.getForComponentName(OTHER_TEST_COMPONENT)).isFalse()
+    }
+
+    @Test
+    fun setTileAdded_differentUsers() {
+        val sharedPrefs0 = FakeSharedPreferences()
+        val sharedPrefs1 = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        underTest.setTileAdded(TEST_COMPONENT, userId = 1, added = true)
+
+        assertThat(sharedPrefs0.getForComponentName(TEST_COMPONENT)).isFalse()
+        assertThat(sharedPrefs1.getForComponentName(TEST_COMPONENT)).isTrue()
+    }
+
+    @Test
+    fun isTileAdded_fromSharedPreferences() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+
+        sharedPrefs.setForComponentName(TEST_COMPONENT, true)
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isTrue()
+
+        sharedPrefs.setForComponentName(TEST_COMPONENT, false)
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+    }
+
+    @Test
+    fun isTileAdded_differentComponents() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        sharedPrefs.setForComponentName(OTHER_TEST_COMPONENT, true)
+
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+        assertThat(underTest.isTileAdded(OTHER_TEST_COMPONENT, userId)).isTrue()
+    }
+
+    @Test
+    fun isTileAdded_differentUsers() {
+        val sharedPrefs0 = FakeSharedPreferences()
+        val sharedPrefs1 = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        sharedPrefs1.setForComponentName(TEST_COMPONENT, true)
+
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 0)).isFalse()
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 1)).isTrue()
+    }
+
+    private fun SharedPreferences.getForComponentName(componentName: ComponentName): Boolean {
+        return getBoolean(componentName.flattenToString(), false)
+    }
+
+    private fun SharedPreferences.setForComponentName(
+        componentName: ComponentName,
+        value: Boolean
+    ) {
+        edit().putBoolean(componentName.flattenToString(), value).commit()
+    }
+
+    companion object {
+        private val TEST_COMPONENT = ComponentName("pkg", "cls")
+        private val OTHER_TEST_COMPONENT = ComponentName("pkg", "other")
+    }
+}
+
+private const val FILE_NAME = "tiles_prefs"
+
+private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
+    UserFileManager {
+    override fun getFile(fileName: String, userId: Int): File {
+        throw UnsupportedOperationException()
+    }
+
+    override fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences {
+        if (fileName != FILE_NAME) {
+            throw IllegalArgumentException("Preference files must be $FILE_NAME")
+        }
+        return sharedPrefs.getValue(userId)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index d3ec1dd..28aeba4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -317,6 +317,26 @@
     }
 
     @Test
+    fun testDisableByPolicyThenRemoved_changesColor() {
+        val stateActive = QSTile.State()
+        stateActive.state = Tile.STATE_ACTIVE
+
+        val stateDisabledByPolicy = stateActive.copy()
+        stateDisabledByPolicy.disabledByPolicy = true
+
+        tileView.changeState(stateActive)
+        val activeColors = tileView.getCurrentColors()
+
+        tileView.changeState(stateDisabledByPolicy)
+        // It has unavailable colors
+        assertThat(tileView.getCurrentColors()).isNotEqualTo(activeColors)
+
+        // When we get back to not disabled by policy tile, it should go back to active colors
+        tileView.changeState(stateActive)
+        assertThat(tileView.getCurrentColors()).containsExactlyElementsIn(activeColors)
+    }
+
+    @Test
     fun testDisabledByPolicy_secondaryLabelText() {
         val testA11yLabel = "TEST_LABEL"
         context.orCreateTestableResources
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 47d88a5..77f7426 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -23,28 +23,21 @@
 import android.graphics.Bitmap
 import android.graphics.Bitmap.Config.HARDWARE
 import android.graphics.ColorSpace
-import android.graphics.Insets
-import android.graphics.Rect
 import android.hardware.HardwareBuffer
 import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.util.ScreenshotRequest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.SCREENSHOT_METADATA_REFACTOR
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argThat
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -61,9 +54,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
-private const val USER_ID = 1
-private const val TASK_ID = 11
-
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class TakeScreenshotServiceTest : SysuiTestCase() {
@@ -123,9 +113,6 @@
             .whenever(requestProcessor)
             .processAsync(/* screenshot= */ any(ScreenshotData::class.java), /* callback= */ any())
 
-        // Flipped in selected test cases
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
         service.attach(
             mContext,
             /* thread = */ null,
@@ -158,39 +145,6 @@
         service.handleRequest(request, { /* onSaved */}, callback)
 
         verify(controller, times(1))
-            .takeScreenshotFullscreen(
-                eq(topComponent),
-                /* onSavedListener = */ any(),
-                /* requestCallback = */ any()
-            )
-
-        assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
-        val logEvent = eventLogger.get(0)
-
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED UiEvent",
-            logEvent.eventId,
-            SCREENSHOT_REQUESTED_KEY_OTHER.id
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            eventLogger.get(0).packageName
-        )
-    }
-
-    @Test
-    fun takeScreenshotFullscreen_screenshotDataEnabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(controller, times(1))
             .handleScreenshot(
                 eq(ScreenshotData.fromRequest(request)),
                 /* onSavedListener = */ any(),
@@ -213,53 +167,7 @@
     }
 
     @Test
-    fun takeScreenshotProvidedImage() {
-        val bounds = Rect(50, 50, 150, 150)
-        val bitmap = makeHardwareBitmap(100, 100)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW)
-                .setTopComponent(topComponent)
-                .setTaskId(TASK_ID)
-                .setUserId(USER_ID)
-                .setBitmap(bitmap)
-                .setBoundsOnScreen(bounds)
-                .setInsets(Insets.NONE)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(controller, times(1))
-            .handleImageAsScreenshot(
-                argThat { b -> b.equalsHardwareBitmap(bitmap) },
-                eq(bounds),
-                eq(Insets.NONE),
-                eq(TASK_ID),
-                eq(USER_ID),
-                eq(topComponent),
-                /* onSavedListener = */ any(),
-                /* requestCallback = */ any()
-            )
-
-        assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
-        val logEvent = eventLogger.get(0)
-
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            logEvent.eventId,
-            SCREENSHOT_REQUESTED_OVERVIEW.id
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            eventLogger.get(0).packageName
-        )
-    }
-
-    @Test
     fun takeScreenshotFullscreen_userLocked() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
         whenever(userManager.isUserUnlocked).thenReturn(false)
 
         val request =
@@ -300,8 +208,6 @@
 
     @Test
     fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
         whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
             .thenReturn(true)
 
@@ -350,143 +256,7 @@
     }
 
     @Test
-    fun takeScreenshotFullscreen_userLocked_metadataDisabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-        whenever(userManager.isUserUnlocked).thenReturn(false)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
-        verify(callback, times(1)).reportError()
-        verifyZeroInteractions(controller)
-
-        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
-        val requestEvent = eventLogger.get(0)
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            SCREENSHOT_REQUESTED_KEY_OTHER.id,
-            requestEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            requestEvent.packageName
-        )
-        val failureEvent = eventLogger.get(1)
-        assertEquals(
-            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
-            SCREENSHOT_CAPTURE_FAILED.id,
-            failureEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            failureEvent.packageName
-        )
-    }
-
-    @Test
-    fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers_metadataDisabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
-        whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
-            .thenReturn(true)
-
-        whenever(
-                devicePolicyResourcesManager.getString(
-                    eq(SCREENSHOT_BLOCKED_BY_ADMIN),
-                    /* Supplier<String> */
-                    any(),
-                )
-            )
-            .thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        // error shown: Toast.makeText(...).show(), untestable
-        verify(callback, times(1)).reportError()
-        verifyZeroInteractions(controller)
-        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
-        val requestEvent = eventLogger.get(0)
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            SCREENSHOT_REQUESTED_KEY_OTHER.id,
-            requestEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            requestEvent.packageName
-        )
-        val failureEvent = eventLogger.get(1)
-        assertEquals(
-            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
-            SCREENSHOT_CAPTURE_FAILED.id,
-            failureEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            failureEvent.packageName
-        )
-    }
-
-    @Test
-    fun takeScreenshot_workProfile_nullBitmap_metadataDisabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        doThrow(IllegalStateException::class.java)
-            .whenever(requestProcessor)
-            .processAsync(any(ScreenshotRequest::class.java), any())
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(callback, times(1)).reportError()
-        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
-        verifyZeroInteractions(controller)
-        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
-        val requestEvent = eventLogger.get(0)
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            SCREENSHOT_REQUESTED_KEY_OTHER.id,
-            requestEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            requestEvent.packageName
-        )
-        val failureEvent = eventLogger.get(1)
-        assertEquals(
-            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
-            SCREENSHOT_CAPTURE_FAILED.id,
-            failureEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            failureEvent.packageName
-        )
-    }
-    @Test
     fun takeScreenshot_workProfile_nullBitmap() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
         val request =
             ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
                 .setTopComponent(topComponent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 374aae1..78f5bf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -122,7 +122,7 @@
             isEnabled = true,
             handleAllUsers = true,
             defaultClockProvider = fakeDefaultProvider,
-            keepAllLoaded = true,
+            keepAllLoaded = false,
             subTag = "Test",
         ) {
             override fun querySettings() { }
@@ -154,8 +154,8 @@
         pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
         val list = registry.getClocks()
         assertEquals(
-            list,
-            listOf(
+            list.toSet(),
+            setOf(
                 ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
                 ClockMetadata("clock_1", "clock 1"),
                 ClockMetadata("clock_2", "clock 2"),
@@ -187,8 +187,8 @@
         pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
         val list = registry.getClocks()
         assertEquals(
-            list,
-            listOf(
+            list.toSet(),
+            setOf(
                 ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
                 ClockMetadata("clock_1", "clock 1"),
                 ClockMetadata("clock_2", "clock 2")
@@ -293,6 +293,53 @@
         assertEquals(4, listChangeCallCount)
     }
 
+    @Test
+    fun pluginAddRemove_concurrentModification() {
+        val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val mockPluginLifecycle3 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val mockPluginLifecycle4 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+        val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+        val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+        val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
+        whenever(mockPluginLifecycle1.isLoaded).thenReturn(true)
+        whenever(mockPluginLifecycle2.isLoaded).thenReturn(true)
+        whenever(mockPluginLifecycle3.isLoaded).thenReturn(true)
+        whenever(mockPluginLifecycle4.isLoaded).thenReturn(true)
+
+        // Set the current clock to the final clock to load
+        registry.applySettings(ClockSettings("clock_4", null))
+        scheduler.runCurrent()
+
+        // When ClockRegistry attempts to unload a plugin, we at that point decide to load and
+        // unload other plugins. This causes ClockRegistry to modify the list of available clock
+        // plugins while it is being iterated over. In production this happens as a result of a
+        // thread race, instead of synchronously like it does here.
+        whenever(mockPluginLifecycle2.unloadPlugin()).then {
+            pluginListener.onPluginDetached(mockPluginLifecycle1)
+            pluginListener.onPluginLoaded(plugin4, mockContext, mockPluginLifecycle4)
+        }
+
+        // Load initial plugins
+        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+        pluginListener.onPluginLoaded(plugin3, mockContext, mockPluginLifecycle3)
+
+        // Repeatedly verify the loaded providers to get final state
+        registry.verifyLoadedProviders()
+        scheduler.runCurrent()
+        registry.verifyLoadedProviders()
+        scheduler.runCurrent()
+
+        // Verify all plugins were correctly loaded into the registry
+        assertEquals(registry.getClocks().toSet(), setOf(
+            ClockMetadata("DEFAULT", "Default Clock"),
+            ClockMetadata("clock_2", "clock 2"),
+            ClockMetadata("clock_3", "clock 3"),
+            ClockMetadata("clock_4", "clock 4")
+        ))
+    }
 
     @Test
     fun jsonDeserialization_gotExpectedObject() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index f581154..f4cd383 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
 import android.os.Bundle;
+import android.view.KeyEvent;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
@@ -397,9 +398,10 @@
 
     @Test
     public void testHandleSysKey() {
-        mCommandQueue.handleSystemKey(1);
+        KeyEvent testEvent = new KeyEvent(1, 1);
+        mCommandQueue.handleSystemKey(testEvent);
         waitForIdleSync();
-        verify(mCallbacks).handleSystemKey(eq(1));
+        verify(mCallbacks).handleSystemKey(eq(testEvent));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 0b202853..1fdcf7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.validMobileEvent
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -89,6 +90,7 @@
 
     private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
     private val mobileMappings = FakeMobileMappingsProxy()
+    private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
 
     private val scope = CoroutineScope(IMMEDIATE)
 
@@ -117,6 +119,7 @@
             MobileConnectionsRepositoryImpl(
                 connectivityRepository,
                 subscriptionManager,
+                subscriptionManagerProxy,
                 telephonyManager,
                 logger,
                 summaryLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index d65277f..ddff17ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
@@ -98,6 +99,7 @@
     @Mock private lateinit var logBufferFactory: TableLogBufferFactory
 
     private val mobileMappings = FakeMobileMappingsProxy()
+    private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
 
     private val scope = CoroutineScope(IMMEDIATE)
 
@@ -179,6 +181,7 @@
             MobileConnectionsRepositoryImpl(
                 connectivityRepository,
                 subscriptionManager,
+                subscriptionManagerProxy,
                 telephonyManager,
                 logger,
                 summaryLogger,
@@ -662,6 +665,8 @@
             var latest: Int? = null
             val job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
 
+            assertThat(latest).isEqualTo(INVALID_SUBSCRIPTION_ID)
+
             fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
                 receiver.onReceive(
                     context,
@@ -686,6 +691,42 @@
         }
 
     @Test
+    fun defaultDataSubId_fetchesInitialValueOnStart() =
+        runBlocking(IMMEDIATE) {
+            subscriptionManagerProxy.defaultDataSubId = 2
+            var latest: Int? = null
+            val job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(2)
+
+            job.cancel()
+        }
+
+    @Test
+    fun defaultDataSubId_fetchesCurrentOnRestart() =
+        runBlocking(IMMEDIATE) {
+            subscriptionManagerProxy.defaultDataSubId = 2
+            var latest: Int? = null
+            var job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(2)
+
+            job.cancel()
+
+            // Collectors go away but come back later
+
+            latest = null
+
+            subscriptionManagerProxy.defaultDataSubId = 1
+
+            job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(1)
+
+            job.cancel()
+        }
+
+    @Test
     fun mobileIsDefault_startsAsFalse() {
         assertThat(underTest.mobileIsDefault.value).isFalse()
     }
@@ -902,6 +943,7 @@
                 MobileConnectionsRepositoryImpl(
                     connectivityRepository,
                     subscriptionManager,
+                    subscriptionManagerProxy,
                     telephonyManager,
                     logger,
                     summaryLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
new file mode 100644
index 0000000..3dc7de6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.util
+
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+
+/** Fake of [SubscriptionManagerProxy] for easy testing */
+class FakeSubscriptionManagerProxy(
+    /** Set the default data subId to be returned in [getDefaultDataSubscriptionId] */
+    var defaultDataSubId: Int = INVALID_SUBSCRIPTION_ID
+) : SubscriptionManagerProxy {
+    override fun getDefaultDataSubscriptionId(): Int = defaultDataSubId
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index 61c1ab7..dfbd61b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -51,6 +51,8 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
@@ -177,6 +179,28 @@
         }
 
     @Test
+    fun onFolded_onScreenTurningOnInvokedTwice_doesNotLogLatency() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.listenForDozing(this)
+            keyguardRepository.setDozing(true)
+            setAodEnabled(enabled = true)
+
+            yield()
+
+            fold()
+            simulateScreenTurningOn()
+            reset(latencyTracker)
+
+            // This can happen > 1 time if the prox sensor is covered
+            simulateScreenTurningOn()
+
+            verify(latencyTracker, never()).onActionStart(any())
+            verify(latencyTracker, never()).onActionEnd(any())
+
+            job.cancel()
+        }
+
+    @Test
     fun onFolded_animationCancelled_doesNotLogLatency() =
         runBlocking(IMMEDIATE) {
             val job = underTest.listenForDozing(this)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index d8b3270..65735f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -21,7 +21,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.flowOf
 
 class FakeBiometricSettingsRepository : BiometricSettingsRepository {
 
@@ -39,12 +38,17 @@
     private val _isStrongBiometricAllowed = MutableStateFlow(false)
     override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow()
 
+    private val _isNonStrongBiometricAllowed = MutableStateFlow(false)
+    override val isNonStrongBiometricAllowed: StateFlow<Boolean>
+        get() = _isNonStrongBiometricAllowed
+
     private val _isFingerprintEnabledByDevicePolicy = MutableStateFlow(false)
     override val isFingerprintEnabledByDevicePolicy =
         _isFingerprintEnabledByDevicePolicy.asStateFlow()
 
+    private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false)
     override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
-        get() = flowOf(true)
+        get() = _isFaceAuthSupportedInCurrentPosture
 
     private val _isCurrentUserInLockdown = MutableStateFlow(false)
     override val isCurrentUserInLockdown: Flow<Boolean>
@@ -66,7 +70,19 @@
         _isFaceEnrolled.value = isFaceEnrolled
     }
 
+    fun setIsFaceAuthSupportedInCurrentPosture(value: Boolean) {
+        _isFaceAuthSupportedInCurrentPosture.value = value
+    }
+
     fun setIsFaceAuthEnabled(enabled: Boolean) {
         _isFaceAuthEnabled.value = enabled
     }
+
+    fun setIsUserInLockdown(value: Boolean) {
+        _isCurrentUserInLockdown.value = value
+    }
+
+    fun setIsNonStrongBiometricAllowed(value: Boolean) {
+        _isNonStrongBiometricAllowed.value = value
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
new file mode 100644
index 0000000..fe94117
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.statusbar.CommandQueue
+import org.mockito.Mockito.mock
+
+class FakeCommandQueue : CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) {
+    private val callbacks = mutableListOf<Callbacks>()
+
+    override fun addCallback(callback: Callbacks) {
+        callbacks.add(callback)
+    }
+
+    override fun removeCallback(callback: Callbacks) {
+        callbacks.remove(callback)
+    }
+
+    fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
+        callbacks.forEach { func(it) }
+    }
+
+    fun callbackCount(): Int = callbacks.size
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 00b1a40..4bfd3d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -30,10 +30,19 @@
     override val isRunning: Flow<Boolean>
         get() = _isRunning
 
-    override val availableFpSensorType: BiometricType?
-        get() = null
+    private var fpSensorType = MutableStateFlow<BiometricType?>(null)
+    override val availableFpSensorType: Flow<BiometricType?>
+        get() = fpSensorType
 
     fun setLockedOut(lockedOut: Boolean) {
         _isLockedOut.value = lockedOut
     }
+
+    fun setIsRunning(value: Boolean) {
+        _isRunning.value = value
+    }
+
+    fun setAvailableFpSensorType(value: BiometricType?) {
+        fpSensorType.value = value
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
index 1dda472..8a6d2aa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
@@ -48,8 +48,6 @@
     override val showMessage = _showMessage.asStateFlow()
     private val _resourceUpdateRequests = MutableStateFlow(false)
     override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
-    override val bouncerPromptReason = 0
-    override val bouncerErrorMessage: CharSequence? = null
     private val _isAlternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = 0L
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 194ed02..d411590 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -129,6 +129,10 @@
         _isKeyguardShowing.value = isShowing
     }
 
+    fun setKeyguardGoingAway(isGoingAway: Boolean) {
+        _isKeyguardGoingAway.value = isGoingAway
+    }
+
     fun setKeyguardOccluded(isOccluded: Boolean) {
         _isKeyguardOccluded.value = isOccluded
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
new file mode 100644
index 0000000..6690de8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeTrustRepository : TrustRepository {
+    private val _isCurrentUserTrusted = MutableStateFlow(false)
+    override val isCurrentUserTrusted: Flow<Boolean>
+        get() = _isCurrentUserTrusted
+
+    fun setCurrentUserTrusted(trust: Boolean) {
+        _isCurrentUserTrusted.value = trust
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
index ced7955..9ff7dd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
@@ -23,11 +23,9 @@
 
 /** A fake [FgsManagerController] to be used in tests. */
 class FakeFgsManagerController(
-    isAvailable: Boolean = true,
     showFooterDot: Boolean = false,
     numRunningPackages: Int = 0,
 ) : FgsManagerController {
-    override val isAvailable: MutableStateFlow<Boolean> = MutableStateFlow(isAvailable)
 
     override var numRunningPackages = numRunningPackages
         set(value) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 53bb340..fbc2381 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -100,4 +100,8 @@
     fun setGuestUserAutoCreated(value: Boolean) {
         _isGuestUserAutoCreated = value
     }
+
+    fun setUserSwitching(value: Boolean) {
+        _userSwitchingInProgress.value = value
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index f3a949d..dd7d38f 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -133,18 +133,6 @@
             @UserIdInt int userId, int associationId) {
         final AssociationInfo association = resolveAssociation(packageName, userId, associationId);
 
-        // Check if the request's data type has been requested before.
-        List<SystemDataTransferRequest> storedRequests =
-                mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
-                        associationId);
-        for (SystemDataTransferRequest storedRequest : storedRequests) {
-            if (storedRequest instanceof PermissionSyncRequest) {
-                Slog.e(LOG_TAG, "The request has been sent before, you can not send "
-                        + "the same request type again.");
-                return null;
-            }
-        }
-
         Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
                 + "] associationId [" + associationId + "]");
 
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 b338d89..1363ef3 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1046,18 +1046,30 @@
      */
     void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration,
             Looper looper) {
+        ArrayList<Integer> displayIdsForUid = getDisplayIdsWhereUidIsRunning(uid);
+        if (displayIdsForUid.isEmpty()) {
+            return;
+        }
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        for (int i = 0; i < displayIdsForUid.size(); i++) {
+            Display display = displayManager.getDisplay(displayIdsForUid.get(i));
+            if (display != null && display.isValid()) {
+                Toast.makeText(mContext.createDisplayContext(display), looper, text,
+                        duration).show();
+            }
+        }
+    }
+
+    private ArrayList<Integer> getDisplayIdsWhereUidIsRunning(int uid) {
+        ArrayList<Integer> displayIdsForUid = new ArrayList<>();
         synchronized (mVirtualDeviceLock) {
-            DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
             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();
-                    }
+                    displayIdsForUid.add(mVirtualDisplays.keyAt(i));
                 }
             }
         }
+        return displayIdsForUid;
     }
 
     boolean isDisplayOwnedByVirtualDevice(int displayId) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e0d1c1e..73dbb86a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -73,6 +73,9 @@
 import android.content.pm.UserInfo;
 import android.content.res.ObbInfo;
 import android.database.ContentObserver;
+import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Binder;
@@ -954,10 +957,27 @@
         }
     }
 
+    private boolean isHevcDecoderSupported() {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (codecInfo.isEncoder()) {
+                continue;
+            }
+            String[] supportedTypes = codecInfo.getSupportedTypes();
+            for (String type : supportedTypes) {
+                if (type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private void configureTranscoding() {
         // See MediaProvider TranscodeHelper#getBooleanProperty for more information
         boolean transcodeEnabled = false;
-        boolean defaultValue = true;
+        boolean defaultValue = isHevcDecoderSupported() ? true : false;
 
         if (SystemProperties.getBoolean("persist.sys.fuse.transcode_user_control", false)) {
             transcodeEnabled = SystemProperties.getBoolean("persist.sys.fuse.transcode_enabled",
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 78d4708..e8c85ce 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -1074,9 +1074,10 @@
                             subGrp, mLastSnapshot, mConfigs.get(subGrp));
                     for (int restrictedTransport : restrictedTransports) {
                         if (ncCopy.hasTransport(restrictedTransport)) {
-                            if (restrictedTransport == TRANSPORT_CELLULAR) {
-                                // Only make a cell network as restricted when the VCN is in
-                                // active mode.
+                            if (restrictedTransport == TRANSPORT_CELLULAR
+                                    || restrictedTransport == TRANSPORT_TEST) {
+                                // For cell or test network, only mark it as restricted when
+                                // the VCN is in active mode.
                                 isRestricted |= (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE);
                             } else {
                                 isRestricted = true;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8fe61e7..78cbf2b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -807,9 +807,9 @@
         ServiceRecord r = res.record;
         // Note, when startService() or startForegroundService() is called on an already
         // running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw
-        // ForegroundServiceStartNotAllowedException), even when the service is alerady timed
-        // out. This is because these APIs will essnetially only change the "started" state
-        // of the service, and it won't afect "the foreground-ness" of the service, or the type
+        // ForegroundServiceStartNotAllowedException), even when the service is already timed
+        // out. This is because these APIs will essentially only change the "started" state
+        // of the service, and it won't affect "the foreground-ness" of the service, or the type
         // of the FGS.
         // However, this call will still _not_ extend the SHORT_SERVICE timeout either.
         // Also, if the app tries to change the type of the FGS later (using
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 9079ba8..5dd0a3f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -16,6 +16,11 @@
 
 package com.android.server.am;
 
+import android.util.Log;
+import android.util.LogWriter;
+
+import java.io.PrintWriter;
+
 /**
  * Common class for the various debug {@link android.util.Log} output configuration in the activity
  * manager package.
@@ -38,6 +43,10 @@
     // Default log tag for the activity manager package.
     static final String TAG_AM = "ActivityManager";
 
+    // Default writer that emits "info" log events for the activity manager package.
+    static final PrintWriter LOG_WRITER_INFO = new PrintWriter(
+            new LogWriter(Log.INFO, TAG_AM));
+
     // Enable all debug log categories.
     static final boolean DEBUG_ALL = false;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6749140..c25e579 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -135,6 +135,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
@@ -6984,8 +6985,9 @@
                 mActivityTaskManager.onScreenAwakeChanged(isAwake);
                 mOomAdjProfiler.onWakefulnessChanged(wakefulness);
                 mOomAdjuster.onWakefulnessChanged(wakefulness);
+
+                updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
             }
-            updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
         }
     }
 
@@ -8645,13 +8647,16 @@
             }
         }
 
+        boolean recoverable = eventType.equals("native_recoverable_crash");
+
         EventLogTags.writeAmCrash(Binder.getCallingPid(),
                 UserHandle.getUserId(Binder.getCallingUid()), processName,
                 r == null ? -1 : r.info.flags,
                 crashInfo.exceptionClassName,
                 crashInfo.exceptionMessage,
                 crashInfo.throwFileName,
-                crashInfo.throwLineNumber);
+                crashInfo.throwLineNumber,
+                recoverable ? 1 : 0);
 
         int processClassEnum = processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
                 : (r != null) ? r.getProcessClassEnum()
@@ -8719,7 +8724,13 @@
                 eventType, r, processName, null, null, null, null, null, null, crashInfo,
                 new Float(loadingProgress), incrementalMetrics, null);
 
-        mAppErrors.crashApplication(r, crashInfo);
+        // For GWP-ASan recoverable crashes, don't make the app crash (the whole point of
+        // 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
+        // debuggerd will terminate the process, but there's a backup where ActivityManager will
+        // also kill it. Avoid that.
+        if (!recoverable) {
+            mAppErrors.crashApplication(r, crashInfo);
+        }
     }
 
     public void handleApplicationStrictModeViolation(
@@ -13734,17 +13745,6 @@
                                 + "RECEIVER_NOT_EXPORTED flag");
             }
 
-            // STOPSHIP(b/259139792): Allow apps that are currently targeting U and in process of
-            // updating their receivers to be exempt from this requirement until their receivers
-            // are flagged.
-            if (requireExplicitFlagForDynamicReceivers) {
-                if ("com.shannon.imsservice".equals(callerPackage)) {
-                    // Note, a versionCode check for this package is not performed because this
-                    // package consumes the SecurityException, so it wouldn't be caught during
-                    // presubmit.
-                    requireExplicitFlagForDynamicReceivers = false;
-                }
-            }
             if (!onlyProtectedBroadcasts) {
                 if (receiver == null && !explicitExportStateDefined) {
                     // sticky broadcast, no flag specified (flag isn't required)
@@ -18713,27 +18713,25 @@
 
     @Override
     public void waitForBroadcastIdle() {
-        waitForBroadcastIdle(/* printWriter= */ null);
+        waitForBroadcastIdle(LOG_WRITER_INFO);
     }
 
-    public void waitForBroadcastIdle(@Nullable PrintWriter pw) {
+    public void waitForBroadcastIdle(@NonNull PrintWriter pw) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
         BroadcastLoopers.waitForIdle(pw);
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.waitForIdle(pw);
         }
-        if (pw != null) {
-            pw.println("All broadcast queues are idle!");
-            pw.flush();
-        }
+        pw.println("All broadcast queues are idle!");
+        pw.flush();
     }
 
     @Override
     public void waitForBroadcastBarrier() {
-        waitForBroadcastBarrier(/* printWriter= */ null, false, false);
+        waitForBroadcastBarrier(LOG_WRITER_INFO, false, false);
     }
 
-    public void waitForBroadcastBarrier(@Nullable PrintWriter pw,
+    public void waitForBroadcastBarrier(@NonNull PrintWriter pw,
             boolean flushBroadcastLoopers, boolean flushApplicationThreads) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
         if (flushBroadcastLoopers) {
@@ -18751,11 +18749,7 @@
      * Wait for all pending {@link IApplicationThread} events to be processed in
      * all currently running apps.
      */
-    public void waitForApplicationBarrier(@Nullable PrintWriter pw) {
-        if (pw == null) {
-            pw = new PrintWriter(new LogWriter(Log.VERBOSE, TAG));
-        }
-
+    public void waitForApplicationBarrier(@NonNull PrintWriter pw) {
         final CountDownLatch finishedLatch = new CountDownLatch(1);
         final AtomicInteger pingCount = new AtomicInteger(0);
         final AtomicInteger pongCount = new AtomicInteger(0);
@@ -18805,15 +18799,18 @@
             try {
                 if (finishedLatch.await(1, TimeUnit.SECONDS)) {
                     pw.println("Finished application barriers!");
+                    pw.flush();
                     return;
                 } else {
                     pw.println("Waiting for application barriers, at " + pongCount.get() + " of "
                             + pingCount.get() + "...");
+                    pw.flush();
                 }
             } catch (InterruptedException ignored) {
             }
         }
         pw.println("Gave up waiting for application barriers!");
+        pw.flush();
     }
 
     void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 72e17d8..6c6ac1e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -38,6 +38,7 @@
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
@@ -112,6 +113,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.DisplayMetrics;
+import android.util.TeeWriter;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.window.SplashScreen;
@@ -3364,11 +3366,13 @@
     }
 
     int runWaitForBroadcastIdle(PrintWriter pw) throws RemoteException {
+        pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
         mInternal.waitForBroadcastIdle(pw);
         return 0;
     }
 
     int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException {
+        pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
         boolean flushBroadcastLoopers = false;
         boolean flushApplicationThreads = false;
         String opt;
@@ -3387,6 +3391,7 @@
     }
 
     int runWaitForApplicationBarrier(PrintWriter pw) throws RemoteException {
+        pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
         mInternal.waitForApplicationBarrier(pw);
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d9ba845..ed297d0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -654,6 +654,7 @@
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             mHandler.post(() -> {
+                mCpuWakeupStats.onUidRemoved(uid);
                 synchronized (mStats) {
                     mStats.removeUidStatsLocked(uid, elapsedRealtime);
                 }
@@ -764,6 +765,7 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
+                mCpuWakeupStats.noteUidProcessState(uid, state);
                 synchronized (mStats) {
                     mStats.noteUidProcessStateLocked(uid, state, elapsedRealtime, uptime);
                 }
diff --git a/services/core/java/com/android/server/am/BroadcastLoopers.java b/services/core/java/com/android/server/am/BroadcastLoopers.java
index a5535cb..92547ea 100644
--- a/services/core/java/com/android/server/am/BroadcastLoopers.java
+++ b/services/core/java/com/android/server/am/BroadcastLoopers.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -74,7 +73,7 @@
      * defined by {@link MessageQueue#isIdle()}. Note that {@link Message#when}
      * still in the future are ignored for the purposes of the idle test.
      */
-    public static void waitForIdle(@Nullable PrintWriter pw) {
+    public static void waitForIdle(@NonNull PrintWriter pw) {
         waitForCondition(pw, (looper, latch) -> {
             final MessageQueue queue = looper.getQueue();
             queue.addIdleHandler(() -> {
@@ -89,7 +88,7 @@
      * Note that {@link Message#when} still in the future are ignored for the purposes
      * of the idle test.
      */
-    public static void waitForBarrier(@Nullable PrintWriter pw) {
+    public static void waitForBarrier(@NonNull PrintWriter pw) {
         waitForCondition(pw, (looper, latch) -> {
             (new Handler(looper)).post(() -> {
                 latch.countDown();
@@ -100,7 +99,7 @@
     /**
      * Wait for all registered {@link Looper} instances to meet a certain condition.
      */
-    private static void waitForCondition(@Nullable PrintWriter pw,
+    private static void waitForCondition(@NonNull PrintWriter pw,
             @NonNull BiConsumer<Looper, CountDownLatch> condition) {
         final CountDownLatch latch;
         synchronized (sLoopers) {
@@ -122,18 +121,12 @@
             final long now = SystemClock.uptimeMillis();
             if (now >= lastPrint + 1000) {
                 lastPrint = now;
-                logv("Waiting for " + latch.getCount() + " loopers to drain...", pw);
+                pw.println("Waiting for " + latch.getCount() + " loopers to drain...");
+                pw.flush();
             }
             SystemClock.sleep(100);
         }
-        logv("Loopers drained!", pw);
-    }
-
-    private static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
-        Slog.v(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
+        pw.println("Loopers drained!");
+        pw.flush();
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 5140f90..8aeae5a 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -861,6 +861,7 @@
     static final int REASON_CONTAINS_RESULT_TO = 15;
     static final int REASON_CONTAINS_INSTRUMENTED = 16;
     static final int REASON_CONTAINS_MANIFEST = 17;
+    static final int REASON_FOREGROUND_ACTIVITIES = 18;
 
     @IntDef(flag = false, prefix = { "REASON_" }, value = {
             REASON_EMPTY,
@@ -880,6 +881,7 @@
             REASON_CONTAINS_RESULT_TO,
             REASON_CONTAINS_INSTRUMENTED,
             REASON_CONTAINS_MANIFEST,
+            REASON_FOREGROUND_ACTIVITIES,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Reason {}
@@ -903,6 +905,7 @@
             case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO";
             case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED";
             case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST";
+            case REASON_FOREGROUND_ACTIVITIES: return "FOREGROUND_ACTIVITIES";
             default: return Integer.toString(reason);
         }
     }
@@ -960,6 +963,11 @@
             } else if (mProcessInstrumented) {
                 mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                 mRunnableAtReason = REASON_INSTRUMENTED;
+            } else if (app != null && app.hasForegroundActivities()) {
+                // TODO: Listen for uid state changes to check when an uid goes in and out of
+                // the TOP state.
+                mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
+                mRunnableAtReason = REASON_FOREGROUND_ACTIVITIES;
             } else if (mCountOrdered > 0) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_ORDERED;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 75e9336..6d1344d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -69,14 +69,6 @@
         Slog.v(TAG, msg);
     }
 
-    static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
-        logv(msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
     static void checkState(boolean expression, @NonNull String msg) {
         if (!expression) {
             throw new IllegalStateException(msg);
@@ -219,7 +211,7 @@
      * since running apps can continue sending new broadcasts in perpetuity;
      * consider using {@link #waitForBarrier} instead.
      */
-    public abstract void waitForIdle(@Nullable PrintWriter pw);
+    public abstract void waitForIdle(@NonNull PrintWriter pw);
 
     /**
      * Wait until any currently waiting broadcasts have been dispatched.
@@ -230,7 +222,7 @@
      * Callers are advised that this method will <em>not</em> wait for any
      * future broadcasts that are newly enqueued after being invoked.
      */
-    public abstract void waitForBarrier(@Nullable PrintWriter pw);
+    public abstract void waitForBarrier(@NonNull PrintWriter pw);
 
     /**
      * Delays delivering broadcasts to the specified package.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 4b8dc99..bd36c3f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -835,7 +835,7 @@
                         OOM_ADJ_REASON_START_RECEIVER);
             }
         } else if (filter.receiverList.app != null) {
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
+            mService.mOomAdjuster.unfreezeTemporarily(filter.receiverList.app,
                     CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
         }
 
@@ -1129,7 +1129,7 @@
                     }
                     if (sendResult) {
                         if (r.callerApp != null) {
-                            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+                            mService.mOomAdjuster.unfreezeTemporarily(
                                     r.callerApp,
                                     CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
                         }
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index e574b49..c56d21c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -29,6 +29,7 @@
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
 import static com.android.server.am.BroadcastProcessQueue.reasonToString;
 import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
@@ -941,7 +942,7 @@
         final ProcessRecord app = r.resultToApp;
         final IApplicationThread thread = (app != null) ? app.getOnewayThread() : null;
         if (thread != null) {
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+            mService.mOomAdjuster.unfreezeTemporarily(
                     app, CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
             if (r.shareIdentity && app.uid != r.callingUid) {
                 mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
@@ -1261,7 +1262,7 @@
      * the given {@link Predicate}.
      */
     private boolean testAllProcessQueues(@NonNull Predicate<BroadcastProcessQueue> test,
-            @NonNull String label, @Nullable PrintWriter pw) {
+            @NonNull String label, @NonNull PrintWriter pw) {
         for (int i = 0; i < mProcessQueues.size(); i++) {
             BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
             while (leaf != null) {
@@ -1269,14 +1270,16 @@
                     final long now = SystemClock.uptimeMillis();
                     if (now > mLastTestFailureTime + DateUtils.SECOND_IN_MILLIS) {
                         mLastTestFailureTime = now;
-                        logv("Test " + label + " failed due to " + leaf.toShortString(), pw);
+                        pw.println("Test " + label + " failed due to " + leaf.toShortString());
+                        pw.flush();
                     }
                     return false;
                 }
                 leaf = leaf.processNameNext;
             }
         }
-        logv("Test " + label + " passed", pw);
+        pw.println("Test " + label + " passed");
+        pw.flush();
         return true;
     }
 
@@ -1374,30 +1377,30 @@
 
     @Override
     public boolean isIdleLocked() {
-        return isIdleLocked(null);
+        return isIdleLocked(LOG_WRITER_INFO);
     }
 
-    public boolean isIdleLocked(@Nullable PrintWriter pw) {
+    public boolean isIdleLocked(@NonNull PrintWriter pw) {
         return testAllProcessQueues(q -> q.isIdle(), "idle", pw);
     }
 
     @Override
     public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime) {
-        return isBeyondBarrierLocked(barrierTime, null);
+        return isBeyondBarrierLocked(barrierTime, LOG_WRITER_INFO);
     }
 
     public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime,
-            @Nullable PrintWriter pw) {
+            @NonNull PrintWriter pw) {
         return testAllProcessQueues(q -> q.isBeyondBarrierLocked(barrierTime), "barrier", pw);
     }
 
     @Override
-    public void waitForIdle(@Nullable PrintWriter pw) {
+    public void waitForIdle(@NonNull PrintWriter pw) {
         waitFor(() -> isIdleLocked(pw));
     }
 
     @Override
-    public void waitForBarrier(@Nullable PrintWriter pw) {
+    public void waitForBarrier(@NonNull PrintWriter pw) {
         final long now = SystemClock.uptimeMillis();
         waitFor(() -> isBeyondBarrierLocked(now, pw));
     }
@@ -1539,7 +1542,7 @@
                 mService.updateLruProcessLocked(queue.app, false, null);
             }
 
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(queue.app,
+            mService.mOomAdjuster.unfreezeTemporarily(queue.app,
                     CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
 
             if (queue.runningOomAdjusted) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 6c9f602..110d69e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -2124,7 +2124,7 @@
                 frozen = opt.isFrozen();
 
                 final UidRecord uidRec = proc.getUidRecord();
-                if (frozen && uidRec.areAllProcessesFrozen()) {
+                if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) {
                     uidRec.setFrozen(true);
                     mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(
                             UID_FROZEN_STATE_CHANGED_MSG, proc));
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 50841ae..81b24215 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -53,7 +53,7 @@
 30037 am_process_start_timeout (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3)
 
 # Unhandled exception
-30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
+30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5),(Recoverable|1|5)
 # Log.wtf() called
 30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3)
 
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 94eb076..cd119e7 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -64,12 +64,15 @@
     class NativeCrashReporter extends Thread {
         ProcessRecord mApp;
         int mSignal;
+        boolean mGwpAsanRecoverableCrash;
         String mCrashReport;
 
-        NativeCrashReporter(ProcessRecord app, int signal, String report) {
+        NativeCrashReporter(ProcessRecord app, int signal, boolean gwpAsanRecoverableCrash,
+                            String report) {
             super("NativeCrashReport");
             mApp = app;
             mSignal = signal;
+            mGwpAsanRecoverableCrash = gwpAsanRecoverableCrash;
             mCrashReport = report;
         }
 
@@ -85,7 +88,9 @@
                 ci.stackTrace = mCrashReport;
 
                 if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
-                mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
+                mAm.handleApplicationCrashInner(
+                        mGwpAsanRecoverableCrash ? "native_recoverable_crash" : "native_crash",
+                        mApp, mApp.processName, ci);
                 if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to report native crash", e);
@@ -207,9 +212,14 @@
             // permits crash_dump to connect to it. This allows us to trust the
             // received values.
 
-            // first, the pid and signal number
-            int headerBytes = readExactly(fd, buf, 0, 8);
-            if (headerBytes != 8) {
+            // Activity Manager protocol:
+            //  - 32-bit network-byte-order: pid
+            //  - 32-bit network-byte-order: signal number
+            //  - byte: gwpAsanRecoverableCrash
+            //  - bytes: raw text of the dump
+            //  - null terminator
+            int headerBytes = readExactly(fd, buf, 0, 9);
+            if (headerBytes != 9) {
                 // protocol failure; give up
                 Slog.e(TAG, "Unable to read from debuggerd");
                 return;
@@ -217,69 +227,76 @@
 
             int pid = unpackInt(buf, 0);
             int signal = unpackInt(buf, 4);
+            boolean gwpAsanRecoverableCrash = buf[8] != 0;
             if (DEBUG) {
-                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
+                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal
+                        + " recoverable=" + gwpAsanRecoverableCrash);
+            }
+            if (pid < 0) {
+                Slog.e(TAG, "Bogus pid!");
+                return;
             }
 
             // now the text of the dump
-            if (pid > 0) {
-                final ProcessRecord pr;
-                synchronized (mAm.mPidsSelfLocked) {
-                    pr = mAm.mPidsSelfLocked.get(pid);
-                }
-                if (pr != null) {
-                    // Don't attempt crash reporting for persistent apps
-                    if (pr.isPersistent()) {
-                        if (DEBUG) {
-                            Slog.v(TAG, "Skipping report for persistent app " + pr);
-                        }
-                        return;
-                    }
-
-                    int bytes;
-                    do {
-                        // get some data
-                        bytes = Os.read(fd, buf, 0, buf.length);
-                        if (bytes > 0) {
-                            if (MORE_DEBUG) {
-                                String s = new String(buf, 0, bytes, "UTF-8");
-                                Slog.v(TAG, "READ=" + bytes + "> " + s);
-                            }
-                            // did we just get the EOD null byte?
-                            if (buf[bytes-1] == 0) {
-                                os.write(buf, 0, bytes-1);  // exclude the EOD token
-                                break;
-                            }
-                            // no EOD, so collect it and read more
-                            os.write(buf, 0, bytes);
-                        }
-                    } while (bytes > 0);
-
-                    // Okay, we've got the report.
-                    if (DEBUG) Slog.v(TAG, "processing");
-
-                    // Mark the process record as being a native crash so that the
-                    // cleanup mechanism knows we're still submitting the report
-                    // even though the process will vanish as soon as we let
-                    // debuggerd proceed.
-                    synchronized (mAm) {
-                        synchronized (mAm.mProcLock) {
-                            pr.mErrorState.setCrashing(true);
-                            pr.mErrorState.setForceCrashReport(true);
-                        }
-                    }
-
-                    // Crash reporting is synchronous but we want to let debuggerd
-                    // go about it business right away, so we spin off the actual
-                    // reporting logic on a thread and let it take it's time.
-                    final String reportString = new String(os.toByteArray(), "UTF-8");
-                    (new NativeCrashReporter(pr, signal, reportString)).start();
-                } else {
-                    Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
-                }
-            } else {
-                Slog.e(TAG, "Bogus pid!");
+            final ProcessRecord pr;
+            synchronized (mAm.mPidsSelfLocked) {
+                pr = mAm.mPidsSelfLocked.get(pid);
             }
+            if (pr == null) {
+                Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
+                return;
+            }
+
+            // Don't attempt crash reporting for persistent apps
+            if (pr.isPersistent()) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Skipping report for persistent app " + pr);
+                }
+                return;
+            }
+
+            int bytes;
+            do {
+                // get some data
+                bytes = Os.read(fd, buf, 0, buf.length);
+                if (bytes > 0) {
+                    if (MORE_DEBUG) {
+                        String s = new String(buf, 0, bytes, "UTF-8");
+                        Slog.v(TAG, "READ=" + bytes + "> " + s);
+                    }
+                    // did we just get the EOD null byte?
+                    if (buf[bytes - 1] == 0) {
+                        os.write(buf, 0, bytes - 1); // exclude the EOD token
+                        break;
+                    }
+                    // no EOD, so collect it and read more
+                    os.write(buf, 0, bytes);
+                }
+            } while (bytes > 0);
+
+            // Okay, we've got the report.
+            if (DEBUG) Slog.v(TAG, "processing");
+
+            // Mark the process record as being a native crash so that the
+            // cleanup mechanism knows we're still submitting the report even
+            // though the process will vanish as soon as we let debuggerd
+            // proceed. This isn't relevant for recoverable crashes, as we don't
+            // show the user an "app crashed" dialogue because the app (by
+            // design) didn't crash.
+            if (!gwpAsanRecoverableCrash) {
+                synchronized (mAm) {
+                    synchronized (mAm.mProcLock) {
+                        pr.mErrorState.setCrashing(true);
+                        pr.mErrorState.setForceCrashReport(true);
+                    }
+                }
+            }
+
+            // Crash reporting is synchronous but we want to let debuggerd
+            // go about it business right away, so we spin off the actual
+            // reporting logic on a thread and let it take it's time.
+            final String reportString = new String(os.toByteArray(), "UTF-8");
+            (new NativeCrashReporter(pr, signal, gwpAsanRecoverableCrash, reportString)).start();
         } catch (Exception e) {
             Slog.e(TAG, "Exception dealing with report", e);
             // ugh, fail.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 84a8099..a86c02d 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -349,6 +349,7 @@
     private final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
     private final ActiveUids mTmpUidRecords;
     private final ArrayDeque<ProcessRecord> mTmpQueue;
+    private final ArraySet<ProcessRecord> mTmpProcessSet = new ArraySet<>();
     private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
     private final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
 
@@ -3472,4 +3473,29 @@
                     CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
         }
     }
+
+    @GuardedBy("mService")
+    void unfreezeTemporarily(ProcessRecord app, @OomAdjuster.OomAdjReason int reason) {
+        if (!mCachedAppOptimizer.useFreezer()) {
+            return;
+        }
+
+        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+        if (!opt.isFrozen() && !opt.isPendingFreeze()) {
+            return;
+        }
+
+        final ArrayList<ProcessRecord> processes = mTmpProcessList;
+        final ActiveUids uids = mTmpUidRecords;
+        mTmpProcessSet.add(app);
+        collectReachableProcessesLocked(mTmpProcessSet, processes, uids);
+        mTmpProcessSet.clear();
+        // Now processes contains app's downstream and app
+        final int size = processes.size();
+        for (int i = 0; i < size; i++) {
+            ProcessRecord proc = processes.get(i);
+            mCachedAppOptimizer.unfreezeTemporarily(proc, reason);
+        }
+        processes.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 1d48cb2..70a696c 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -22,7 +22,9 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.ProcessRecord.TAG;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AnrController;
 import android.app.ApplicationErrorReport;
@@ -56,6 +58,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ResourcePressureUtil;
 import com.android.server.criticalevents.CriticalEventLog;
+import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.server.wm.WindowProcessController;
 
 import java.io.File;
@@ -396,6 +399,8 @@
                 });
             }
         }
+        // Build memory headers for the ANRing process.
+        String memoryHeaders = buildMemoryHeadersFor(pid);
 
         // Get critical event log before logging the ANR so that it doesn't occur in the log.
         latencyTracker.criticalEventLogStarted();
@@ -496,7 +501,7 @@
         File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
                 isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
                 nativePidsFuture, tracesFileException, firstPidEndOffset, annotation,
-                criticalEventLog, auxiliaryTaskExecutor, latencyTracker);
+                criticalEventLog, memoryHeaders, auxiliaryTaskExecutor, latencyTracker);
 
         if (isMonitorCpuUsage()) {
             // Wait for the first call to finish
@@ -710,6 +715,27 @@
             resolver.getUserId()) != 0;
     }
 
+    private @Nullable String buildMemoryHeadersFor(int pid) {
+        if (pid <= 0) {
+            Slog.i(TAG, "Memory header requested with invalid pid: " + pid);
+            return null;
+        }
+        MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+        if (snapshot == null) {
+            Slog.i(TAG, "Failed to get memory snapshot for pid:" + pid);
+            return null;
+        }
+
+        StringBuilder memoryHeaders = new StringBuilder();
+        memoryHeaders.append("RssHwmKb: ")
+            .append(snapshot.rssHighWaterMarkInKilobytes)
+            .append("\n");
+        memoryHeaders.append("RssKb: ").append(snapshot.rssInKilobytes).append("\n");
+        memoryHeaders.append("RssAnonKb: ").append(snapshot.anonRssInKilobytes).append("\n");
+        memoryHeaders.append("RssShmemKb: ").append(snapshot.rssShmemKilobytes).append("\n");
+        memoryHeaders.append("VmSwapKb: ").append(snapshot.swapInKilobytes).append("\n");
+        return memoryHeaders.toString();
+    }
     /**
      * Unless configured otherwise, swallow ANRs in background processes & kill the process.
      * Non-private access is for tests only.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 50d00b4..e651e23 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1050,6 +1050,11 @@
         return mState.isCached();
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    public boolean hasForegroundActivities() {
+        return mState.hasForegroundActivities();
+    }
+
     boolean hasActivities() {
         return mWindowProcessController.hasActivities();
     }
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index 9373328..10ddc2f 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -85,7 +85,8 @@
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
             @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
         return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
-                logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, latencyTracker);
+                logExceptionCreatingFile, null, null, null, null, auxiliaryTaskExecutor,
+                latencyTracker);
     }
 
     /**
@@ -99,7 +100,7 @@
             AnrLatencyTracker latencyTracker) {
         return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
                 logExceptionCreatingFile, null, subject, criticalEventSection,
-                auxiliaryTaskExecutor, latencyTracker);
+                /* memoryHeaders= */ null, auxiliaryTaskExecutor, latencyTracker);
     }
 
     /**
@@ -110,7 +111,8 @@
             ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
             Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
             AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
-            @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
+            String memoryHeaders, @NonNull Executor auxiliaryTaskExecutor,
+            AnrLatencyTracker latencyTracker) {
         try {
 
             if (latencyTracker != null) {
@@ -150,9 +152,10 @@
                 return null;
             }
 
-            if (subject != null || criticalEventSection != null) {
+            if (subject != null || criticalEventSection != null || memoryHeaders != null) {
                 appendtoANRFile(tracesFile.getAbsolutePath(),
-                        (subject != null ? "Subject: " + subject + "\n\n" : "")
+                        (subject != null ? "Subject: " + subject + "\n" : "")
+                        + (memoryHeaders != null ? memoryHeaders + "\n\n" : "")
                         + (criticalEventSection != null ? criticalEventSection : ""));
             }
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 965a07b..81ba4b8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1548,19 +1548,20 @@
     }
 
     private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
-        final int callingUid = Binder.getCallingUid();
         // We get to access everything
-        if (callingUid == Process.myPid()) {
+        final int callingPid = Binder.getCallingPid();
+        if (callingPid == Process.myPid()) {
             return;
         }
         // Apps can access their own data
+        final int callingUid = Binder.getCallingUid();
         if (uid == callingUid && packageName != null
                 && checkPackage(uid, packageName) == MODE_ALLOWED) {
             return;
         }
         // Otherwise, you need a permission...
-        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
-                Binder.getCallingPid(), callingUid, null);
+        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, callingPid,
+                callingUid, null);
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 6758581..43063af 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1359,6 +1359,9 @@
                         "LE Audio device addr=" + address + " now available").printLog(TAG));
             }
 
+            // Reset LEA suspend state each time a new sink is connected
+            mAudioSystem.setParameters("LeAudioSuspended=false");
+
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
                     new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
             mDeviceBroker.postAccessoryPlugMediaUnmute(device);
@@ -1404,6 +1407,9 @@
 
     @GuardedBy("mDevicesLock")
     private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
+        // prevent any activity on the LEA output to avoid unwanted
+        // reconnection of the sink.
+        mAudioSystem.setParameters("LeAudioSuspended=true");
         // the device will be made unavailable later, so consider it disconnected right away
         mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
         // send the delayed message to make the device unavailable later
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9886faf7..d43687b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -929,7 +929,7 @@
 
     // Defines the format for the connection "address" for ALSA devices
     public static String makeAlsaAddressString(int card, int device) {
-        return "card=" + card + ";device=" + device + ";";
+        return "card=" + card + ";device=" + device;
     }
 
     public static final class Lifecycle extends SystemService {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 2dcdc54..631d7f5 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -493,6 +493,7 @@
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
         AudioSystem.setParameters("A2dpSuspended=false");
+        AudioSystem.setParameters("LeAudioSuspended=false");
         mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
new file mode 100644
index 0000000..f0e4b0f5
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+final class HardwareKeyboardShortcutController {
+    @GuardedBy("ImfLock.class")
+    private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
+
+    @GuardedBy("ImfLock.class")
+    void reset(@NonNull InputMethodUtils.InputMethodSettings settings) {
+        mSubtypeHandles.clear();
+        for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
+            if (!imi.shouldShowInInputMethodPicker()) {
+                continue;
+            }
+            final List<InputMethodSubtype> subtypes =
+                    settings.getEnabledInputMethodSubtypeListLocked(imi, true);
+            if (subtypes.isEmpty()) {
+                mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, null));
+            } else {
+                for (final InputMethodSubtype subtype : subtypes) {
+                    if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
+                        mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, subtype));
+                    }
+                }
+            }
+        }
+    }
+
+    @AnyThread
+    @Nullable
+    static <T> T getNeighborItem(@NonNull List<T> list, @NonNull T value, boolean next) {
+        final int size = list.size();
+        for (int i = 0; i < size; ++i) {
+            if (Objects.equals(value, list.get(i))) {
+                final int nextIndex = (i + (next ? 1 : -1) + size) % size;
+                return list.get(nextIndex);
+            }
+        }
+        return null;
+    }
+
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    InputMethodSubtypeHandle onSubtypeSwitch(
+            @NonNull InputMethodSubtypeHandle currentImeAndSubtype, boolean forward) {
+        return getNeighborItem(mSubtypeHandles, currentImeAndSubtype, forward);
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b440208..2433211 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -316,6 +316,8 @@
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
     final InputMethodSubtypeSwitchingController mSwitchingController;
+    final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
+            new HardwareKeyboardShortcutController();
 
     /**
      * Tracks how many times {@link #mMethodMap} was updated.
@@ -1731,6 +1733,7 @@
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController =
                 InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
+        mHardwareKeyboardShortcutController.reset(mSettings);
         mMenuController = new InputMethodMenuController(this);
         mBindingController =
                 bindingControllerForTesting != null
@@ -3268,6 +3271,7 @@
         // TODO: Make sure that mSwitchingController and mSettings are sharing the
         // the same enabled IMEs list.
         mSwitchingController.resetCircularListLocked(mContext);
+        mHardwareKeyboardShortcutController.reset(mSettings);
 
         sendOnNavButtonFlagsChangedLocked();
     }
@@ -5293,6 +5297,7 @@
         // TODO: Make sure that mSwitchingController and mSettings are sharing the
         // the same enabled IMEs list.
         mSwitchingController.resetCircularListLocked(mContext);
+        mHardwareKeyboardShortcutController.reset(mSettings);
 
         sendOnNavButtonFlagsChangedLocked();
 
@@ -5827,10 +5832,37 @@
         @Override
         public void switchKeyboardLayout(int direction) {
             synchronized (ImfLock.class) {
-                if (direction > 0) {
-                    switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */);
-                } else {
-                    // TODO(b/258853866): Support backwards switching.
+                final InputMethodInfo currentImi = mMethodMap.get(getSelectedMethodIdLocked());
+                if (currentImi == null) {
+                    return;
+                }
+                final InputMethodSubtypeHandle currentSubtypeHandle =
+                        InputMethodSubtypeHandle.of(currentImi, mCurrentSubtype);
+                final InputMethodSubtypeHandle nextSubtypeHandle =
+                        mHardwareKeyboardShortcutController.onSubtypeSwitch(currentSubtypeHandle,
+                                direction > 0);
+                if (nextSubtypeHandle == null) {
+                    return;
+                }
+                final InputMethodInfo nextImi = mMethodMap.get(nextSubtypeHandle.getImeId());
+                if (nextImi == null) {
+                    return;
+                }
+
+                final int subtypeCount = nextImi.getSubtypeCount();
+                if (subtypeCount == 0) {
+                    if (nextSubtypeHandle.equals(InputMethodSubtypeHandle.of(nextImi, null))) {
+                        setInputMethodLocked(nextImi.getId(), NOT_A_SUBTYPE_ID);
+                    }
+                    return;
+                }
+
+                for (int i = 0; i < subtypeCount; ++i) {
+                    if (nextSubtypeHandle.equals(
+                            InputMethodSubtypeHandle.of(nextImi, nextImi.getSubtypeAt(i)))) {
+                        setInputMethodLocked(nextImi.getId(), i);
+                        return;
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index e48412a..82b4da3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -103,6 +103,7 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1396,11 +1397,14 @@
             Log.v(TAG, "SV count: " + gnssStatus.getSatelliteCount());
         }
 
+        Set<Pair<Integer, Integer>> satellites = new HashSet<>();
         int usedInFixCount = 0;
         int maxCn0 = 0;
         int meanCn0 = 0;
         for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) {
             if (gnssStatus.usedInFix(i)) {
+                satellites.add(
+                        new Pair<>(gnssStatus.getConstellationType(i), gnssStatus.getSvid(i)));
                 ++usedInFixCount;
                 if (gnssStatus.getCn0DbHz(i) > maxCn0) {
                     maxCn0 = (int) gnssStatus.getCn0DbHz(i);
@@ -1413,7 +1417,7 @@
             meanCn0 /= usedInFixCount;
         }
         // return number of sats used in fix instead of total reported
-        mLocationExtras.set(usedInFixCount, meanCn0, maxCn0);
+        mLocationExtras.set(satellites.size(), meanCn0, maxCn0);
 
         mGnssMetrics.logSvStatus(gnssStatus);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 65dcec7..9ec9ff5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -656,7 +656,6 @@
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
     private boolean mLockScreenAllowSecureNotifications = true;
-    boolean mAllowFgsDismissal = false;
     boolean mSystemExemptFromDismissal = false;
 
     private static final int MY_UID = Process.myUid();
@@ -2581,19 +2580,9 @@
             for (String name : properties.getKeyset()) {
                 if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
                     mAssistants.resetDefaultAssistantsIfNecessary();
-                } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
-                    String value = properties.getString(name, null);
-                    if ("true".equals(value)) {
-                        mAllowFgsDismissal = true;
-                    } else if ("false".equals(value)) {
-                        mAllowFgsDismissal = false;
-                    }
                 }
             }
         };
-        mAllowFgsDismissal = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true);
         mSystemExemptFromDismissal = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
                 /* name= */ "application_exemptions",
@@ -7736,9 +7725,6 @@
                     // flags are set.
                     if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
                         notification.flags |= FLAG_NO_CLEAR;
-                        if (!mAllowFgsDismissal) {
-                            notification.flags |= FLAG_ONGOING_EVENT;
-                        }
                     }
 
                     mRankingHelper.extractSignals(r);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1cfcb4e..c9a6c63 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -545,6 +545,7 @@
         pw.println(prefix + "mAdjustments=" + mAdjustments);
         pw.println(prefix + "shortcut=" + notification.getShortcutId()
                 + " found valid? " + (mShortcutInfo != null));
+        pw.println(prefix + "mUserVisOverride=" + getPackageVisibilityOverride());
     }
 
     private void dumpNotification(PrintWriter pw, String prefix, Notification notification,
@@ -574,6 +575,7 @@
         } else {
             pw.println("null");
         }
+        pw.println(prefix + "vis=" + notification.visibility);
         pw.println(prefix + "contentView=" + formatRemoteViews(notification.contentView));
         pw.println(prefix + "bigContentView=" + formatRemoteViews(notification.bigContentView));
         pw.println(prefix + "headsUpContentView="
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2038e79..6b213b7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7208,6 +7208,7 @@
      * TODO: In the meantime, can this be moved to a schedule call?
      * TODO(b/182523293): This should be removed once we finish migration of permission storage.
      */
+    @SuppressWarnings("GuardedBy")
     void writeSettingsLPrTEMP(boolean sync) {
         snapshotComputer(false);
         mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
@@ -7257,6 +7258,10 @@
     static boolean isPreapprovalRequestAvailable() {
         final long token = Binder.clearCallingIdentity();
         try {
+            if (!Resources.getSystem().getBoolean(
+                    com.android.internal.R.bool.config_isPreApprovalRequestAvailable)) {
+                return false;
+            }
             return DeviceConfig.getBoolean(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                     PROPERTY_IS_PRE_APPROVAL_REQUEST_AVAILABLE, true /* defaultValue */);
         } finally {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 927a722..ab9d1cf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -278,6 +278,7 @@
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
     static final int WRITE_USER_MSG = 1;
+    static final int WRITE_USER_LIST_MSG = 2;
     static final int WRITE_USER_DELAY = 2*1000;  // 2 seconds
 
     private static final long BOOT_USER_SET_TIMEOUT_MS = 300_000;
@@ -321,7 +322,6 @@
     private final Handler mHandler;
 
     private final File mUsersDir;
-    @GuardedBy("mPackagesLock")
     private final File mUserListFile;
 
     private final IBinder mUserRestrictionToken = new Binder();
@@ -3623,77 +3623,95 @@
         mUpdatingSystemUserMode = true;
     }
 
+
+    private ResilientAtomicFile getUserListFile() {
+        File tempBackup = new File(mUserListFile.getParent(), mUserListFile.getName() + ".backup");
+        File reserveCopy = new File(mUserListFile.getParent(),
+                mUserListFile.getName() + ".reservecopy");
+        int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+        return new ResilientAtomicFile(mUserListFile, tempBackup, reserveCopy, fileMode,
+                "user list", (priority, msg) -> {
+            Slog.e(LOG_TAG, msg);
+            // Something went wrong, schedule full rewrite.
+            scheduleWriteUserList();
+        });
+    }
+
     @GuardedBy({"mPackagesLock"})
     private void readUserListLP() {
-        if (!mUserListFile.exists()) {
-            fallbackToSingleUserLP();
-            return;
-        }
-        FileInputStream fis = null;
-        AtomicFile userListFile = new AtomicFile(mUserListFile);
-        try {
-            fis = userListFile.openRead();
-            final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
-            int type;
-            while ((type = parser.next()) != XmlPullParser.START_TAG
-                    && type != XmlPullParser.END_DOCUMENT) {
-                // Skip
-            }
+        try (ResilientAtomicFile file = getUserListFile()) {
+            FileInputStream fin = null;
+            try {
+                fin = file.openRead();
+                if (fin == null) {
+                    Slog.e(LOG_TAG, "userlist.xml not found, fallback to single user");
+                    fallbackToSingleUserLP();
+                    return;
+                }
 
-            if (type != XmlPullParser.START_TAG) {
-                Slog.e(LOG_TAG, "Unable to read user list");
-                fallbackToSingleUserLP();
-                return;
-            }
+                final TypedXmlPullParser parser = Xml.resolvePullParser(fin);
+                int type;
+                while ((type = parser.next()) != XmlPullParser.START_TAG
+                        && type != XmlPullParser.END_DOCUMENT) {
+                    // Skip
+                }
 
-            mNextSerialNumber = -1;
-            if (parser.getName().equals(TAG_USERS)) {
-                mNextSerialNumber =
-                        parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
-                mUserVersion =
-                        parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
-                mUserTypeVersion =
-                        parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
-            }
+                if (type != XmlPullParser.START_TAG) {
+                    Slog.e(LOG_TAG, "Unable to read user list");
+                    fallbackToSingleUserLP();
+                    return;
+                }
 
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                if (type == XmlPullParser.START_TAG) {
-                    final String name = parser.getName();
-                    if (name.equals(TAG_USER)) {
-                        UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+                mNextSerialNumber = -1;
+                if (parser.getName().equals(TAG_USERS)) {
+                    mNextSerialNumber =
+                            parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
+                    mUserVersion =
+                            parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
+                    mUserTypeVersion =
+                            parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+                }
 
-                        if (userData != null) {
-                            synchronized (mUsersLock) {
-                                mUsers.put(userData.info.id, userData);
-                                if (mNextSerialNumber < 0
-                                        || mNextSerialNumber <= userData.info.id) {
-                                    mNextSerialNumber = userData.info.id + 1;
-                                }
-                            }
-                        }
-                    } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
-                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                                && type != XmlPullParser.END_TAG) {
-                            if (type == XmlPullParser.START_TAG) {
-                                if (parser.getName().equals(TAG_RESTRICTIONS)) {
-                                    synchronized (mGuestRestrictions) {
-                                        UserRestrictionsUtils
-                                                .readRestrictions(parser, mGuestRestrictions);
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                    if (type == XmlPullParser.START_TAG) {
+                        final String name = parser.getName();
+                        if (name.equals(TAG_USER)) {
+                            UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+
+                            if (userData != null) {
+                                synchronized (mUsersLock) {
+                                    mUsers.put(userData.info.id, userData);
+                                    if (mNextSerialNumber < 0
+                                            || mNextSerialNumber <= userData.info.id) {
+                                        mNextSerialNumber = userData.info.id + 1;
                                     }
                                 }
-                                break;
+                            }
+                        } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
+                            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                                    && type != XmlPullParser.END_TAG) {
+                                if (type == XmlPullParser.START_TAG) {
+                                    if (parser.getName().equals(TAG_RESTRICTIONS)) {
+                                        synchronized (mGuestRestrictions) {
+                                            UserRestrictionsUtils
+                                                    .readRestrictions(parser, mGuestRestrictions);
+                                        }
+                                    }
+                                    break;
+                                }
                             }
                         }
                     }
                 }
-            }
 
-            updateUserIds();
-            upgradeIfNecessaryLP();
-        } catch (IOException | XmlPullParserException e) {
-            fallbackToSingleUserLP();
-        } finally {
-            IoUtils.closeQuietly(fis);
+                updateUserIds();
+                upgradeIfNecessaryLP();
+            } catch (Exception e) {
+                // Remove corrupted file and retry.
+                file.failRead(fin, e);
+                readUserListLP();
+                return;
+            }
         }
 
         synchronized (mUsersLock) {
@@ -4099,6 +4117,18 @@
         }
     }
 
+    private void scheduleWriteUserList() {
+        if (DBG) {
+            debug("scheduleWriteUserList");
+        }
+        // No need to wrap it within a lock -- worst case, we'll just post the same message
+        // twice.
+        if (!mHandler.hasMessages(WRITE_USER_LIST_MSG)) {
+            Message msg = mHandler.obtainMessage(WRITE_USER_LIST_MSG);
+            mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
+        }
+    }
+
     private void scheduleWriteUser(UserData userData) {
         if (DBG) {
             debug("scheduleWriteUser");
@@ -4111,20 +4141,37 @@
         }
     }
 
+    private ResilientAtomicFile getUserFile(int userId) {
+        File file = new File(mUsersDir, userId + XML_SUFFIX);
+        File tempBackup = new File(mUsersDir, userId + XML_SUFFIX + ".backup");
+        File reserveCopy = new File(mUsersDir, userId + XML_SUFFIX + ".reservecopy");
+        int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+        return new ResilientAtomicFile(file, tempBackup, reserveCopy, fileMode,
+                "user info", (priority, msg) -> {
+            Slog.e(LOG_TAG, msg);
+            // Something went wrong, schedule full rewrite.
+            UserData userData = getUserDataNoChecks(userId);
+            if (userData != null) {
+                scheduleWriteUser(userData);
+            }
+        });
+    }
+
     @GuardedBy({"mPackagesLock"})
     private void writeUserLP(UserData userData) {
         if (DBG) {
             debug("writeUserLP " + userData);
         }
-        FileOutputStream fos = null;
-        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userData.info.id + XML_SUFFIX));
-        try {
-            fos = userFile.startWrite();
-            writeUserLP(userData, fos);
-            userFile.finishWrite(fos);
-        } catch (Exception ioe) {
-            Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
-            userFile.failWrite(fos);
+        try (ResilientAtomicFile userFile = getUserFile(userData.info.id)) {
+            FileOutputStream fos = null;
+            try {
+                fos = userFile.startWrite();
+                writeUserLP(userData, fos);
+                userFile.finishWrite(fos);
+            } catch (Exception ioe) {
+                Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
+                userFile.failWrite(fos);
+            }
         }
     }
 
@@ -4253,65 +4300,71 @@
         if (DBG) {
             debug("writeUserList");
         }
-        FileOutputStream fos = null;
-        AtomicFile userListFile = new AtomicFile(mUserListFile);
-        try {
-            fos = userListFile.startWrite();
-            final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
-            serializer.startTag(null, TAG_USERS);
-            serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
-            serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
-            serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+        try (ResilientAtomicFile file = getUserListFile()) {
+            FileOutputStream fos = null;
+            try {
+                fos = file.startWrite();
 
-            serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
-            synchronized (mGuestRestrictions) {
-                UserRestrictionsUtils
-                        .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
-            }
-            serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            int[] userIdsToWrite;
-            synchronized (mUsersLock) {
-                userIdsToWrite = new int[mUsers.size()];
-                for (int i = 0; i < userIdsToWrite.length; i++) {
-                    UserInfo user = mUsers.valueAt(i).info;
-                    userIdsToWrite[i] = user.id;
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
+                serializer.startDocument(null, true);
+                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
+                        true);
+
+                serializer.startTag(null, TAG_USERS);
+                serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
+                serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
+                serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+
+                serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+                synchronized (mGuestRestrictions) {
+                    UserRestrictionsUtils
+                            .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
                 }
-            }
-            for (int id : userIdsToWrite) {
-                serializer.startTag(null, TAG_USER);
-                serializer.attributeInt(null, ATTR_ID, id);
-                serializer.endTag(null, TAG_USER);
-            }
+                serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
+                int[] userIdsToWrite;
+                synchronized (mUsersLock) {
+                    userIdsToWrite = new int[mUsers.size()];
+                    for (int i = 0; i < userIdsToWrite.length; i++) {
+                        UserInfo user = mUsers.valueAt(i).info;
+                        userIdsToWrite[i] = user.id;
+                    }
+                }
+                for (int id : userIdsToWrite) {
+                    serializer.startTag(null, TAG_USER);
+                    serializer.attributeInt(null, ATTR_ID, id);
+                    serializer.endTag(null, TAG_USER);
+                }
 
-            serializer.endTag(null, TAG_USERS);
+                serializer.endTag(null, TAG_USERS);
 
-            serializer.endDocument();
-            userListFile.finishWrite(fos);
-        } catch (Exception e) {
-            userListFile.failWrite(fos);
-            Slog.e(LOG_TAG, "Error writing user list");
+                serializer.endDocument();
+                file.finishWrite(fos);
+            } catch (Exception e) {
+                Slog.e(LOG_TAG, "Error writing user list", e);
+                file.failWrite(fos);
+            }
         }
     }
 
     @GuardedBy({"mPackagesLock"})
     private UserData readUserLP(int id) {
-        FileInputStream fis = null;
-        try {
-            AtomicFile userFile =
-                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + XML_SUFFIX));
-            fis = userFile.openRead();
-            return readUserLP(id, fis);
-        } catch (IOException ioe) {
-            Slog.e(LOG_TAG, "Error reading user list");
-        } catch (XmlPullParserException pe) {
-            Slog.e(LOG_TAG, "Error reading user list");
-        } finally {
-            IoUtils.closeQuietly(fis);
+        try (ResilientAtomicFile file = getUserFile(id)) {
+            FileInputStream fis = null;
+            try {
+                fis = file.openRead();
+                if (fis == null) {
+                    Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id);
+                    return null;
+                }
+                return readUserLP(id, fis);
+            } catch (Exception e) {
+                // Remove corrupted file and retry.
+                Slog.e(LOG_TAG, "Error reading user info, user id: " + id);
+                file.failRead(fis, e);
+                return readUserLP(id);
+            }
         }
-        return null;
     }
 
     @GuardedBy({"mPackagesLock"})
@@ -5805,9 +5858,8 @@
         synchronized (mPackagesLock) {
             writeUserListLP();
         }
-        // Remove user file
-        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userId + XML_SUFFIX));
-        userFile.delete();
+        // Remove user file(s)
+        getUserFile(userId).delete();
         updateUserIds();
         if (RELEASE_DELETED_USER_ID) {
             synchronized (mUsersLock) {
@@ -6770,6 +6822,13 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case WRITE_USER_LIST_MSG: {
+                    removeMessages(WRITE_USER_LIST_MSG);
+                    synchronized (mPackagesLock) {
+                        writeUserListLP();
+                    }
+                    break;
+                }
                 case WRITE_USER_MSG:
                     removeMessages(WRITE_USER_MSG, msg.obj);
                     synchronized (mPackagesLock) {
@@ -6782,6 +6841,7 @@
                                     + ", it was probably removed before handler could handle it");
                         }
                     }
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index f340f93..c9ebeae 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -652,20 +652,33 @@
 
     private boolean isAdbVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
             boolean requestedDisableVerification) {
+        boolean verifierIncludeAdb = android.provider.Settings.Global.getInt(
+                mPm.mContext.getContentResolver(),
+                android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+
         if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
+            if (!verifierIncludeAdb) {
+                Slog.w(TAG, "Force verification of ADB install because of user restriction.");
+            }
             return true;
         }
-        // Check if the developer wants to skip verification for ADB installs
+
+        // Check if the verification disabled globally, first.
+        if (!verifierIncludeAdb) {
+            return false;
+        }
+
+        // Check if the developer wants to skip verification for ADB installs.
         if (requestedDisableVerification) {
             if (!packageExists(pkgInfoLite.packageName)) {
-                // Always verify fresh install
+                // Always verify fresh install.
                 return true;
             }
-            // Only skip when apk is debuggable
+            // Only skip when apk is debuggable.
             return !pkgInfoLite.debuggable;
         }
-        return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
-                android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b56e5c9..c5f939a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -64,6 +64,7 @@
 import android.permission.PermissionCheckerManager;
 import android.permission.PermissionManager;
 import android.permission.PermissionManagerInternal;
+import android.service.voice.VoiceInteractionManagerInternal;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -969,12 +970,13 @@
             // the private data in your process; or by you explicitly calling to another
             // app passing the source, in which case you must trust the other side;
 
-            final int callingUid = Binder.getCallingUid();
-            if (source.getUid() != callingUid && mContext.checkPermission(
+            final int callingUid = resolveUid(Binder.getCallingUid());
+            final int sourceUid = resolveUid(source.getUid());
+            if (sourceUid != callingUid && mContext.checkPermission(
                     Manifest.permission.UPDATE_APP_OPS_STATS, /*pid*/ -1, callingUid)
                     != PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException("Cannot register attribution source for uid:"
-                        + source.getUid() + " from uid:" + callingUid);
+                        + sourceUid + " from uid:" + callingUid);
             }
 
             final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -983,10 +985,10 @@
             // TODO(b/234653108): Clean up this UID/package & cross-user check.
             // If calling from the system process, allow registering attribution for package from
             // any user
-            int userId = UserHandle.getUserId((callingUid == Process.SYSTEM_UID ? source.getUid()
+            int userId = UserHandle.getUserId((callingUid == Process.SYSTEM_UID ? sourceUid
                     : callingUid));
             if (packageManagerInternal.getPackageUid(source.getPackageName(), 0, userId)
-                    != source.getUid()) {
+                    != sourceUid) {
                 throw new SecurityException("Cannot register attribution source for package:"
                         + source.getPackageName() + " from uid:" + callingUid);
             }
@@ -1012,6 +1014,21 @@
                 return false;
             }
         }
+
+        private int resolveUid(int uid) {
+            final VoiceInteractionManagerInternal vimi = LocalServices
+                    .getService(VoiceInteractionManagerInternal.class);
+            if (vimi == null) {
+                return uid;
+            }
+            final VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity
+                    hotwordDetectionServiceIdentity = vimi.getHotwordDetectionServiceIdentity();
+            if (hotwordDetectionServiceIdentity != null
+                    && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) {
+                return hotwordDetectionServiceIdentity.getOwnerUid();
+            }
+            return uid;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 54f87d0..8165958 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -729,7 +729,7 @@
                     mAutofillManagerInternal.onBackKeyPressed();
                     break;
                 case MSG_SYSTEM_KEY_PRESS:
-                    sendSystemKeyToStatusBar(msg.arg1);
+                    sendSystemKeyToStatusBar((KeyEvent) msg.obj);
                     break;
                 case MSG_HANDLE_ALL_APPS:
                     launchAllAppsAction();
@@ -949,7 +949,7 @@
         final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
 
         // Inform the StatusBar; but do not allow it to consume the event.
-        sendSystemKeyToStatusBarAsync(event.getKeyCode());
+        sendSystemKeyToStatusBarAsync(event);
 
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
@@ -3001,7 +3001,11 @@
                 break;
             case KeyEvent.KEYCODE_N:
                 if (down && event.isMetaPressed()) {
-                    toggleNotificationPanel();
+                    if (event.isCtrlPressed()) {
+                        sendSystemKeyToStatusBarAsync(event);
+                    } else {
+                        toggleNotificationPanel();
+                    }
                     return key_consumed;
                 }
                 break;
@@ -3569,14 +3573,16 @@
 
     @Override
     public int applyKeyguardOcclusionChange() {
-        if (mKeyguardOccludedChanged) {
-            if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
-                    + mPendingKeyguardOccluded);
-            if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
-                return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
-            }
+        if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
+                + mPendingKeyguardOccluded);
+
+        // TODO(b/276433230): Explicitly save before/after for occlude state in each
+        // Transition so we don't need to update SysUI every time.
+        if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
+            return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
+        } else {
+            return 0;
         }
-        return 0;
     }
 
     /**
@@ -3854,6 +3860,7 @@
     private boolean setKeyguardOccludedLw(boolean isOccluded) {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
         mKeyguardOccludedChanged = false;
+        mPendingKeyguardOccluded = isOccluded;
         mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
         return mKeyguardDelegate.isShowing();
     }
@@ -4119,7 +4126,7 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 if (down) {
-                    sendSystemKeyToStatusBarAsync(event.getKeyCode());
+                    sendSystemKeyToStatusBarAsync(event);
 
                     NotificationManager nm = getNotificationService();
                     if (nm != null && !mHandleVolumeKeysInWM) {
@@ -4397,7 +4404,7 @@
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
                 if (down && mStylusButtonsEnabled) {
-                    sendSystemKeyToStatusBarAsync(keyCode);
+                    sendSystemKeyToStatusBarAsync(event);
                 }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
@@ -4494,7 +4501,7 @@
             if (!mAccessibilityManager.isEnabled()
                     || !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
                 if (mSystemNavigationKeysEnabled) {
-                    sendSystemKeyToStatusBarAsync(event.getKeyCode());
+                    sendSystemKeyToStatusBarAsync(event);
                 }
             }
         }
@@ -4503,11 +4510,11 @@
     /**
      * Notify the StatusBar that a system key was pressed.
      */
-    private void sendSystemKeyToStatusBar(int keyCode) {
+    private void sendSystemKeyToStatusBar(KeyEvent key) {
         IStatusBarService statusBar = getStatusBarService();
         if (statusBar != null) {
             try {
-                statusBar.handleSystemKey(keyCode);
+                statusBar.handleSystemKey(key);
             } catch (RemoteException e) {
                 // Oh well.
             }
@@ -4517,8 +4524,8 @@
     /**
      * Notify the StatusBar that a system key was pressed without blocking the current thread.
      */
-    private void sendSystemKeyToStatusBarAsync(int keyCode) {
-        Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyCode, 0);
+    private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) {
+        Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyEvent);
         message.setAsynchronous(true);
         mHandler.sendMessage(message);
     }
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index d55fbc2..231ffc6 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -20,6 +20,7 @@
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -27,11 +28,10 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.IndentingPrintWriter;
-import android.util.IntArray;
-import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.SparseLongArray;
 import android.util.TimeSparseArray;
 import android.util.TimeUtils;
@@ -59,7 +59,7 @@
     private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
     @VisibleForTesting
     static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
-    private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2);
+    private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
 
     private final Handler mHandler;
     private final IrqDeviceMap mIrqDeviceMap;
@@ -69,10 +69,15 @@
 
     @VisibleForTesting
     final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+
+    /* Maps timestamp -> {subsystem  -> {uid -> procState}} */
     @VisibleForTesting
-    final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution =
+    final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
             new TimeSparseArray<>();
 
+    final SparseIntArray mUidProcStates = new SparseIntArray();
+    private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4);
+
     public CpuWakeupStats(Context context, int mapRes, Handler handler) {
         mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
         mHandler = handler;
@@ -102,13 +107,14 @@
                     FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN,
                     FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN,
                     null,
-                    wakeupToLog.mElapsedMillis);
+                    wakeupToLog.mElapsedMillis,
+                    null);
             Trace.instantForTrack(Trace.TRACE_TAG_POWER, TRACE_TRACK_WAKEUP_ATTRIBUTION,
                     wakeupToLog.mElapsedMillis + " --");
             return;
         }
 
-        final SparseArray<SparseBooleanArray> wakeupAttribution = mWakeupAttribution.get(
+        final SparseArray<SparseIntArray> wakeupAttribution = mWakeupAttribution.get(
                 wakeupToLog.mElapsedMillis);
         if (wakeupAttribution == null) {
             // This is not expected but can theoretically happen in extreme situations, e.g. if we
@@ -121,24 +127,28 @@
 
         for (int i = 0; i < wakeupAttribution.size(); i++) {
             final int subsystem = wakeupAttribution.keyAt(i);
-            final SparseBooleanArray uidMap = wakeupAttribution.valueAt(i);
+            final SparseIntArray uidProcStates = wakeupAttribution.valueAt(i);
             final int[] uids;
-            if (uidMap == null || uidMap.size() == 0) {
-                uids = new int[0];
+            final int[] procStatesProto;
+
+            if (uidProcStates == null || uidProcStates.size() == 0) {
+                uids = procStatesProto = new int[0];
             } else {
-                final IntArray tmp = new IntArray(uidMap.size());
-                for (int j = 0; j < uidMap.size(); j++) {
-                    if (uidMap.valueAt(j)) {
-                        tmp.add(uidMap.keyAt(j));
-                    }
+                final int numUids = uidProcStates.size();
+                uids = new int[numUids];
+                procStatesProto = new int[numUids];
+                for (int j = 0; j < numUids; j++) {
+                    uids[j] = uidProcStates.keyAt(j);
+                    procStatesProto[j] = ActivityManager.processStateAmToProto(
+                            uidProcStates.valueAt(j));
                 }
-                uids = tmp.toArray();
             }
             FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED,
                     FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ,
                     subsystemToStatsReason(subsystem),
                     uids,
-                    wakeupToLog.mElapsedMillis);
+                    wakeupToLog.mElapsedMillis,
+                    procStatesProto);
 
             if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
                 if (i == 0) {
@@ -154,6 +164,20 @@
                 traceEventBuilder.toString().trim());
     }
 
+    /**
+     * Clean up data for a uid that is being removed.
+     */
+    public synchronized void onUidRemoved(int uid) {
+        mUidProcStates.delete(uid);
+    }
+
+    /**
+     * Notes a procstate change for the given uid to maintain the mapping internally.
+     */
+    public synchronized void noteUidProcessState(int uid, int state) {
+        mUidProcStates.put(uid, state);
+    }
+
     /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
     public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
             String rawReason) {
@@ -184,8 +208,17 @@
 
     /** Notes a waking activity that could have potentially woken up the CPU. */
     public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) {
-        if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) {
-            mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids);
+        if (uids == null) {
+            return;
+        }
+        mReusableUidProcStates.clear();
+        for (int i = 0; i < uids.length; i++) {
+            mReusableUidProcStates.put(uids[i],
+                    mUidProcStates.get(uids[i], ActivityManager.PROCESS_STATE_UNKNOWN));
+        }
+        if (!attemptAttributionWith(subsystem, elapsedRealtime, mReusableUidProcStates)) {
+            mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime,
+                    mReusableUidProcStates);
         }
     }
 
@@ -196,7 +229,7 @@
             return;
         }
 
-        SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
+        SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
         if (attribution == null) {
             attribution = new SparseArray<>();
             mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
@@ -210,14 +243,14 @@
             final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
             final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
 
-            final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
+            final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
                     startTime, endTime);
             attribution.put(subsystem, uidsToBlame);
         }
     }
 
     private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
-            int... uids) {
+            SparseIntArray uidProcStates) {
         final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
                 activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
         final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
@@ -233,19 +266,19 @@
             if (subsystems.get(subsystem)) {
                 // We don't expect more than one wakeup to be found within such a short window, so
                 // just attribute this one and exit
-                SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+                SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(
                         wakeup.mElapsedMillis);
                 if (attribution == null) {
                     attribution = new SparseArray<>();
                     mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
                 }
-                SparseBooleanArray uidsToBlame = attribution.get(subsystem);
+                SparseIntArray uidsToBlame = attribution.get(subsystem);
                 if (uidsToBlame == null) {
-                    uidsToBlame = new SparseBooleanArray(uids.length);
-                    attribution.put(subsystem, uidsToBlame);
-                }
-                for (final int uid : uids) {
-                    uidsToBlame.put(uid, true);
+                    attribution.put(subsystem, uidProcStates.clone());
+                } else {
+                    for (int i = 0; i < uidProcStates.size(); i++) {
+                        uidsToBlame.put(uidProcStates.keyAt(i), uidProcStates.valueAt(i));
+                    }
                 }
                 return true;
             }
@@ -267,6 +300,19 @@
         mRecentWakingActivity.dump(pw, nowElapsed);
         pw.println();
 
+        pw.println("Current proc-state map (" + mUidProcStates.size() + "):");
+        pw.increaseIndent();
+        for (int i = 0; i < mUidProcStates.size(); i++) {
+            if (i > 0) {
+                pw.print(", ");
+            }
+            UserHandle.formatUid(pw, mUidProcStates.keyAt(i));
+            pw.print(":" + ActivityManager.procStateToString(mUidProcStates.valueAt(i)));
+        }
+        pw.println();
+        pw.decreaseIndent();
+        pw.println();
+
         final SparseLongArray attributionStats = new SparseLongArray();
         pw.println("Wakeup events:");
         pw.increaseIndent();
@@ -278,7 +324,7 @@
             final Wakeup wakeup = mWakeupEvents.valueAt(i);
             pw.println(wakeup);
             pw.print("Attribution: ");
-            final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+            final SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(
                     wakeup.mElapsedMillis);
             if (attribution == null) {
                 pw.println("N/A");
@@ -292,15 +338,17 @@
                     int attributed = IntPair.first(counters);
                     final int total = IntPair.second(counters) + 1;
 
-                    pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx)));
-                    pw.print(", uids: [");
-                    final SparseBooleanArray uids = attribution.valueAt(subsystemIdx);
-                    if (uids != null) {
-                        for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) {
+                    pw.print(subsystemToString(attribution.keyAt(subsystemIdx)));
+                    pw.print(" [");
+                    final SparseIntArray uidProcStates = attribution.valueAt(subsystemIdx);
+                    if (uidProcStates != null) {
+                        for (int uidIdx = 0; uidIdx < uidProcStates.size(); uidIdx++) {
                             if (uidIdx > 0) {
                                 pw.print(", ");
                             }
-                            UserHandle.formatUid(pw, uids.keyAt(uidIdx));
+                            UserHandle.formatUid(pw, uidProcStates.keyAt(uidIdx));
+                            pw.print(" " + ActivityManager.procStateToString(
+                                    uidProcStates.valueAt(uidIdx)));
                         }
                         attributed++;
                     }
@@ -330,29 +378,39 @@
         pw.println();
     }
 
+    /**
+     * This class stores recent unattributed activity history per subsystem.
+     * The activity is stored as a mapping of subsystem to timestamp to uid to procstate.
+     */
     private static final class WakingActivityHistory {
         private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
 
-        private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
+        private SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity =
                 new SparseArray<>();
 
-        void recordActivity(int subsystem, long elapsedRealtime, int... uids) {
-            if (uids == null) {
+        void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) {
+            if (uidProcStates == null) {
                 return;
             }
-            TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem);
+            TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
             if (wakingActivity == null) {
                 wakingActivity = new TimeSparseArray<>();
                 mWakingActivity.put(subsystem, wakingActivity);
             }
-            SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime);
+            final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime);
             if (uidsToBlame == null) {
-                uidsToBlame = new SparseBooleanArray(uids.length);
+                wakingActivity.put(elapsedRealtime, uidProcStates.clone());
+            } else {
+                for (int i = 0; i < uidProcStates.size(); i++) {
+                    final int uid = uidProcStates.keyAt(i);
+                    // Just in case there are duplicate uids reported with the same timestamp,
+                    // keep the processState which was reported first.
+                    if (uidsToBlame.indexOfKey(uid) < 0) {
+                        uidsToBlame.put(uid, uidProcStates.valueAt(i));
+                    }
+                }
                 wakingActivity.put(elapsedRealtime, uidsToBlame);
             }
-            for (int i = 0; i < uids.length; i++) {
-                uidsToBlame.put(uids[i], true);
-            }
             // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS.
             // Note that the last activity is always present, even if it occurred before
             // WAKING_ACTIVITY_RETENTION_MS.
@@ -365,7 +423,7 @@
 
         void clearAllBefore(long elapsedRealtime) {
             for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
-                final TimeSparseArray<SparseBooleanArray> activityPerSubsystem =
+                final TimeSparseArray<SparseIntArray> activityPerSubsystem =
                         mWakingActivity.valueAt(subsystemIdx);
                 final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
                 for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
@@ -377,20 +435,20 @@
             }
         }
 
-        SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
-            final SparseBooleanArray uidsToReturn = new SparseBooleanArray();
+        SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
+            final SparseIntArray uidsToReturn = new SparseIntArray();
 
-            final TimeSparseArray<SparseBooleanArray> activityForSubsystem =
+            final TimeSparseArray<SparseIntArray> activityForSubsystem =
                     mWakingActivity.get(subsystem);
             if (activityForSubsystem != null) {
                 final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
                 final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
                 for (int i = endIdx; i >= startIdx; i--) {
-                    final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i);
+                    final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i);
                     for (int j = 0; j < uidsForTime.size(); j++) {
-                        if (uidsForTime.valueAt(j)) {
-                            uidsToReturn.put(uidsForTime.keyAt(j), true);
-                        }
+                        // In case the same uid appears in different uidsForTime maps, there is no
+                        // good way to choose one processState, so just arbitrarily pick any.
+                        uidsToReturn.put(uidsForTime.keyAt(j), uidsForTime.valueAt(j));
                     }
                 }
                 // More efficient to remove in a separate loop as it avoids repeatedly calling gc().
@@ -409,25 +467,23 @@
             pw.increaseIndent();
             for (int i = 0; i < mWakingActivity.size(); i++) {
                 pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
-                final LongSparseArray<SparseBooleanArray> wakingActivity =
-                        mWakingActivity.valueAt(i);
+                final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
                 if (wakingActivity == null) {
                     continue;
                 }
                 pw.increaseIndent();
                 for (int j = wakingActivity.size() - 1; j >= 0; j--) {
                     TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw);
-                    final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j);
+                    final SparseIntArray uidsToBlame = wakingActivity.valueAt(j);
                     if (uidsToBlame == null) {
                         pw.println();
                         continue;
                     }
                     pw.print(": ");
                     for (int k = 0; k < uidsToBlame.size(); k++) {
-                        if (uidsToBlame.valueAt(k)) {
-                            UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
-                            pw.print(", ");
-                        }
+                        UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
+                        pw.print(" [" + ActivityManager.procStateToString(uidsToBlame.valueAt(k)));
+                        pw.print("], ");
                     }
                     pw.println();
                 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 1358417..2638f34 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -360,7 +360,7 @@
                     sb.append("ALL");
                 }
                 sb.append("[");
-                for (int i = 0; i < expectedLength; i++) {
+                for (int i = 0; i < energyConsumerIds.length; i++) {
                     final int id = energyConsumerIds[i];
                     sb.append(id);
                     sb.append("(type:");
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 35e88c1..363d2fd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -84,6 +84,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
@@ -902,12 +903,12 @@
     }
 
     @Override
-    public void handleSystemKey(int key) throws RemoteException {
+    public void handleSystemKey(KeyEvent key) throws RemoteException {
         if (!checkCanCollapseStatusBar("handleSystemKey")) {
             return;
         }
 
-        mLastSystemKey = key;
+        mLastSystemKey = key.getKeyCode();
 
         if (mBar != null) {
             try {
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 2141eba..7f129ea 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -171,6 +171,18 @@
             return false;
         }
 
+        for (Map.Entry<Integer, Integer> entry :
+                networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
+            final int cap = entry.getKey();
+            final int matchCriteria = entry.getValue();
+
+            if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
+                return false;
+            } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
+                return false;
+            }
+        }
+
         if (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST)) {
             return true;
         }
@@ -319,18 +331,6 @@
             return false;
         }
 
-        for (Map.Entry<Integer, Integer> entry :
-                networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
-            final int cap = entry.getKey();
-            final int matchCriteria = entry.getValue();
-
-            if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
-                return false;
-            } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
-                return false;
-            }
-        }
-
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 81dabfd..5f56923 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -179,6 +179,9 @@
 import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_SEND_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
 import static com.android.server.wm.ActivityRecordProto.STARTING_MOVED;
@@ -6046,6 +6049,8 @@
             // An activity must be in the {@link PAUSING} state for the system to validate
             // the move to {@link PAUSED}.
             setState(PAUSING, "makeActiveIfNeeded");
+            EventLogTags.writeWmPauseActivity(mUserId, System.identityHashCode(this),
+                    shortComponentName, "userLeaving=false", "make-active");
             try {
                 mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
                         PauseActivityItem.obtain(finishing, false /* userLeaving */,
@@ -10226,6 +10231,12 @@
         proto.write(LAST_DROP_INPUT_MODE, mLastDropInputMode);
         proto.write(OVERRIDE_ORIENTATION, getOverrideOrientation());
         proto.write(SHOULD_SEND_COMPAT_FAKE_FOCUS, shouldSendCompatFakeFocus());
+        proto.write(SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT,
+                mLetterboxUiController.shouldForceRotateForCameraCompat());
+        proto.write(SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT,
+                mLetterboxUiController.shouldRefreshActivityForCameraCompat());
+        proto.write(SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT,
+                mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bbdaa24..12fe6a0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -100,6 +100,7 @@
 import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
 import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
@@ -2011,7 +2012,7 @@
             return;
         }
 
-        if (r == mRootWindowContainer.getTopResumedActivity()) {
+        if (r.isState(RESUMED) && r == mRootWindowContainer.getTopResumedActivity()) {
             setLastResumedActivityUncheckLocked(r, "setFocusedTask-alreadyTop");
             return;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index feaec37..be503fc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1562,7 +1562,6 @@
     }
 
     private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) {
-        rootTask.mTransitionController.requestCloseTransitionIfNeeded(rootTask);
         /**
          * Workaround: Force-stop all the activities in the root pinned task before we reparent them
          * to the fullscreen root task.  This is to guarantee that when we are removing a root task,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a44f25c..89cb13a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5153,12 +5153,12 @@
 
         @Override
         void updateAboveInsetsState(InsetsState aboveInsetsState,
-                SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+                SparseArray<InsetsSource> localInsetsSourcesFromParent,
                 ArraySet<WindowState> insetsChangedWindows) {
             if (skipImeWindowsDuringTraversal(mDisplayContent)) {
                 return;
             }
-            super.updateAboveInsetsState(aboveInsetsState, localInsetsSourceProvidersFromParent,
+            super.updateAboveInsetsState(aboveInsetsState, localInsetsSourcesFromParent,
                     insetsChangedWindows);
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 22dd0e5..d31fe23 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1981,6 +1981,14 @@
             return;
         }
 
+        if (controlTarget != null) {
+            final WindowState win = controlTarget.getWindow();
+
+            if (win != null && win.isActivityTypeDream()) {
+                return;
+            }
+        }
+
         final @InsetsType int restorePositionTypes = (Type.statusBars() | Type.navigationBars())
                 & controlTarget.getRequestedVisibleTypes();
 
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 052c09a..d65f464 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -99,23 +99,6 @@
         }
     }
 
-    WindowState getHostWindow(IBinder inputToken) {
-        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
-        return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
-    }
-
-    boolean isOverlay(IBinder inputToken) {
-        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
-        return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
-    }
-
-    void setIsOverlay(IBinder focusGrantToken) {
-        EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken);
-        if (embeddedWindow != null) {
-            embeddedWindow.setIsOverlay();
-        }
-    }
-
     void remove(IWindow client) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
             EmbeddedWindow ew = mWindows.valueAt(i);
@@ -176,14 +159,15 @@
         public Session mSession;
         InputChannel mInputChannel;
         final int mWindowType;
-        // Track whether the EmbeddedWindow is a system hosted overlay via
-        // {@link OverlayHost}. In the case of client hosted overlays, the client
-        // view hierarchy will take care of invoking requestEmbeddedWindowFocus
-        // but for system hosted overlays we have to do this via tapOutsideDetection
-        // and this variable is mostly used for tracking that.
-        boolean mIsOverlay = false;
 
-        private IBinder mFocusGrantToken;
+        /**
+         * A unique token associated with the embedded window that can be used by the host window
+         * to request focus transfer to the embedded. This is not the input token since we don't
+         * want to give clients access to each others input token.
+         */
+        private final IBinder mFocusGrantToken;
+
+        private boolean mIsFocusable;
 
         /**
          * @param session  calling session to check ownership of the window
@@ -199,7 +183,8 @@
          */
         EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
                        WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
-                       int displayId, IBinder focusGrantToken, String inputHandleName) {
+                       int displayId, IBinder focusGrantToken, String inputHandleName,
+                       boolean isFocusable) {
             mSession = session;
             mWmService = service;
             mClient = clientToken;
@@ -214,6 +199,7 @@
             final String hostWindowName =
                     (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
                             : "";
+            mIsFocusable = isFocusable;
             mName = "Embedded{" + inputHandleName + hostWindowName + "}";
         }
 
@@ -279,13 +265,6 @@
             return mOwnerUid;
         }
 
-        void setIsOverlay() {
-            mIsOverlay = true;
-        }
-        boolean getIsOverlay() {
-            return mIsOverlay;
-        }
-
         IBinder getFocusGrantToken() {
             return mFocusGrantToken;
         }
@@ -297,20 +276,33 @@
             return null;
         }
 
+        void setIsFocusable(boolean isFocusable) {
+            mIsFocusable = isFocusable;
+        }
+
         /**
-         * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
-         * so we need to participate inside handlePointerDownOutsideFocus logic
-         * however client hosted overlays will rely on the hosting view hierarchy
-         * to grant and revoke focus, and so the server side logic is not needed.
+         * When an embedded window is touched when it's not currently focus, we need to switch
+         * focus to that embedded window unless the embedded window was marked as not focusable.
          */
         @Override
         public boolean receiveFocusFromTapOutside() {
-            return mIsOverlay;
+            return mIsFocusable;
         }
 
         private void handleTap(boolean grantFocus) {
             if (mInputChannel != null) {
-                mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+                if (mHostWindowState != null) {
+                    mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient,
+                            mFocusGrantToken, grantFocus);
+                    if (grantFocus) {
+                        // If granting focus to the embedded when tapped, we need to ensure the host
+                        // gains focus as well or the transfer won't take effect since it requires
+                        // the host to transfer the focus to the embedded.
+                        mHostWindowState.handleTapOutsideFocusInsideSelf();
+                    }
+                } else {
+                    mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 4be98a3..b4dffdc 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -48,7 +48,7 @@
  * Controller for IME inset source on the server. It's called provider as it provides the
  * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
  */
-final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider {
+final class ImeInsetsSourceProvider extends InsetsSourceProvider {
 
     /** The token tracking the current IME request or {@code null} otherwise. */
     @Nullable
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 301c184..3d4e0eb 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -289,6 +289,14 @@
         mChanged = true;
     }
 
+    void setFocusTransferTarget(IBinder toToken) {
+        if (mHandle.focusTransferTarget == toToken) {
+            return;
+        }
+        mHandle.focusTransferTarget = toToken;
+        mChanged = true;
+    }
+
     @Override
     public String toString() {
         return mHandle + ", changed=" + mChanged;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index a8c9cd3..fe13b87 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -223,10 +223,10 @@
 
         startAnimation(false /* show */, () -> {
             synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                final SparseArray<WindowContainerInsetsSourceProvider> providers =
+                final SparseArray<InsetsSourceProvider> providers =
                         mStateController.getSourceProviders();
                 for (int i = providers.size() - 1; i >= 0; i--) {
-                    final WindowContainerInsetsSourceProvider provider = providers.valueAt(i);
+                    final InsetsSourceProvider provider = providers.valueAt(i);
                     if (!isTransient(provider.getSource().getType())) {
                         continue;
                     }
@@ -341,11 +341,10 @@
             }
         }
 
-        final SparseArray<WindowContainerInsetsSourceProvider> providers =
-                mStateController.getSourceProviders();
+        final SparseArray<InsetsSourceProvider> providers = mStateController.getSourceProviders();
         final int windowType = attrs.type;
         for (int i = providers.size() - 1; i >= 0; i--) {
-            final WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
+            final InsetsSourceProvider otherProvider = providers.valueAt(i);
             if (otherProvider.overridesFrame(windowType)) {
                 if (state == originalState) {
                     state = new InsetsState(state);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 0953604..3b23f97 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -58,7 +58,7 @@
  * Controller for a specific inset source on the server. It's called provider as it provides the
  * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
  */
-abstract class InsetsSourceProvider {
+class InsetsSourceProvider {
 
     protected final DisplayContent mDisplayContent;
     protected final @NonNull InsetsSource mSource;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index e4ffb8d..249ead0 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -56,7 +56,7 @@
     private final InsetsState mState = new InsetsState();
     private final DisplayContent mDisplayContent;
 
-    private final SparseArray<WindowContainerInsetsSourceProvider> mProviders = new SparseArray<>();
+    private final SparseArray<InsetsSourceProvider> mProviders = new SparseArray<>();
     private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
             mControlTargetProvidersMap = new ArrayMap<>();
     private final SparseArray<InsetsControlTarget> mIdControlTargetMap = new SparseArray<>();
@@ -106,22 +106,22 @@
         return result;
     }
 
-    SparseArray<WindowContainerInsetsSourceProvider> getSourceProviders() {
+    SparseArray<InsetsSourceProvider> getSourceProviders() {
         return mProviders;
     }
 
     /**
      * @return The provider of a specific source ID.
      */
-    WindowContainerInsetsSourceProvider getOrCreateSourceProvider(int id, @InsetsType int type) {
-        WindowContainerInsetsSourceProvider provider = mProviders.get(id);
+    InsetsSourceProvider getOrCreateSourceProvider(int id, @InsetsType int type) {
+        InsetsSourceProvider provider = mProviders.get(id);
         if (provider != null) {
             return provider;
         }
         final InsetsSource source = mState.getOrCreateSource(id, type);
         provider = id == ID_IME
                 ? new ImeInsetsSourceProvider(source, this, mDisplayContent)
-                : new WindowContainerInsetsSourceProvider(source, this, mDisplayContent);
+                : new InsetsSourceProvider(source, this, mDisplayContent);
         mProviders.put(id, provider);
         return provider;
     }
@@ -161,14 +161,15 @@
         final InsetsState aboveInsetsState = new InsetsState();
         aboveInsetsState.set(mState,
                 displayCutout() | systemGestures() | mandatorySystemGestures());
+        final SparseArray<InsetsSource> localInsetsSourcesFromParent = new SparseArray<>();
         final ArraySet<WindowState> insetsChangedWindows = new ArraySet<>();
-        final SparseArray<InsetsSourceProvider>
-                localInsetsSourceProvidersFromParent = new SparseArray<>();
+
         // This method will iterate on the entire hierarchy in top to bottom z-order manner. The
         // aboveInsetsState will be modified as per the insets provided by the WindowState being
         // visited.
-        mDisplayContent.updateAboveInsetsState(aboveInsetsState,
-                localInsetsSourceProvidersFromParent, insetsChangedWindows);
+        mDisplayContent.updateAboveInsetsState(aboveInsetsState, localInsetsSourcesFromParent,
+                insetsChangedWindows);
+
         if (notifyInsetsChange) {
             for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
                 mDispatchInsetsChanged.accept(insetsChangedWindows.valueAt(i));
@@ -333,7 +334,7 @@
         }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             for (int i = mProviders.size() - 1; i >= 0; i--) {
-                final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
+                final InsetsSourceProvider provider = mProviders.valueAt(i);
                 provider.onSurfaceTransactionApplied();
             }
             final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
deleted file mode 100644
index 6e8beee..0000000
--- a/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
+++ /dev/null
@@ -1,53 +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.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.InsetsSource;
-
-/**
- * An {@link InsetsSourceProvider} which doesn't have a backing window or a window container.
- */
-public class RectInsetsSourceProvider extends InsetsSourceProvider {
-    private static final String TAG = TAG_WITH_CLASS_NAME
-            ? RectInsetsSourceProvider.class.getSimpleName()
-            : TAG_WM;
-
-    RectInsetsSourceProvider(InsetsSource source,
-            InsetsStateController stateController, DisplayContent displayContent) {
-        super(source, stateController, displayContent);
-    }
-
-    /**
-     * Sets the given {@code rect} as the frame of the underlying {@link InsetsSource}.
-     */
-    void setRect(Rect rect) {
-        mSource.setFrame(rect);
-        mSource.setVisible(true);
-    }
-
-    @Override
-    void onPostLayout() {
-        if (WindowManagerDebugConfig.DEBUG) {
-            Slog.d(TAG, "onPostLayout(), not calling super.onPostLayout().");
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b7e2265..db44532 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4687,6 +4687,7 @@
         if (!isAttached()) {
             return;
         }
+        mTransitionController.collect(this);
 
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 683767e..0fe1f92 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -50,6 +50,7 @@
 import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
 import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
@@ -183,6 +184,12 @@
     private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
 
     /**
+     * The (non alwaysOnTop) tasks which were on-top of their display before the transition. If
+     * tasks are nested, all the tasks that are parents of the on-top task are also included.
+     */
+    private final ArrayList<Task> mOnTopTasksStart = new ArrayList<>();
+
+    /**
      * Set of participating windowtokens (activity/wallpaper) which are visible at the end of
      * the transition animation.
      */
@@ -515,6 +522,7 @@
         mParticipants.add(wc);
         if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
             mTargetDisplays.add(wc.getDisplayContent());
+            addOnTopTasks(wc.getDisplayContent(), mOnTopTasksStart);
         }
         if (info.mShowWallpaper) {
             // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
@@ -526,6 +534,27 @@
         }
     }
 
+    /** Adds the top non-alwaysOnTop tasks within `task` to `out`. */
+    private static void addOnTopTasks(Task task, ArrayList<Task> out) {
+        for (int i = task.getChildCount() - 1; i >= 0; --i) {
+            final Task child = task.getChildAt(i).asTask();
+            if (child == null) return;
+            if (child.getWindowConfiguration().isAlwaysOnTop()) continue;
+            out.add(child);
+            addOnTopTasks(child, out);
+            break;
+        }
+    }
+
+    /** Get the top non-alwaysOnTop leaf task on the display `dc`. */
+    private static void addOnTopTasks(DisplayContent dc, ArrayList<Task> out) {
+        final Task topNotAlwaysOnTop = dc.getRootTask(
+                t -> !t.getWindowConfiguration().isAlwaysOnTop());
+        if (topNotAlwaysOnTop == null) return;
+        out.add(topNotAlwaysOnTop);
+        addOnTopTasks(topNotAlwaysOnTop, out);
+    }
+
     /**
      * Records wc as changing its state of existence during this transition. For example, a new
      * task is considered an existence change while moving a task to front is not. wc is added
@@ -783,7 +812,9 @@
      *         a chance we won't thus legacy-entry (via pause+userLeaving) will return false.
      */
     private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar) {
-        if (!mCanPipOnFinish || !ar.isVisible() || ar.getTask() == null) return false;
+        if (!mCanPipOnFinish || !ar.isVisible() || ar.getTask() == null || !ar.isState(RESUMED)) {
+            return false;
+        }
 
         if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
             if (didCommitTransientLaunch()) {
@@ -796,18 +827,14 @@
         }
 
         // Legacy pip-entry (not via isAutoEnterEnabled).
-        boolean canPip = ar.getDeferHidingClient();
-        if (!canPip && didCommitTransientLaunch()) {
+        if (didCommitTransientLaunch() && ar.supportsPictureInPicture()) {
             // force enable pip-on-task-switch now that we've committed to actually launching to the
             // transient activity, and then recalculate whether we can attempt pip.
             ar.supportsEnterPipOnTaskSwitch = true;
-            canPip = ar.checkEnterPictureInPictureState(
-                    "finishTransition", true /* beforeStopping */)
-                    && ar.isState(RESUMED);
         }
-        if (!canPip) return false;
+
         try {
-            // Legacy PIP-enter requires pause event with user-leaving.
+            // If not going auto-pip, the activity should be paused with user-leaving.
             mController.mAtm.mTaskSupervisor.mUserLeaving = true;
             ar.getTaskFragment().startPausing(false /* uiSleeping */,
                     null /* resuming */, "finishTransition");
@@ -851,6 +878,7 @@
 
         boolean hasParticipatedDisplay = false;
         boolean hasVisibleTransientLaunch = false;
+        boolean enterAutoPip = false;
         // Commit all going-invisible containers
         for (int i = 0; i < mParticipants.size(); ++i) {
             final WindowContainer<?> participant = mParticipants.valueAt(i);
@@ -886,6 +914,8 @@
                         }
                         ar.commitVisibility(false /* visible */, false /* performLayout */,
                                 true /* fromTransition */);
+                    } else {
+                        enterAutoPip = true;
                     }
                 }
                 if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) {
@@ -940,8 +970,10 @@
         }
 
         if (hasVisibleTransientLaunch) {
-            // Notify the change about the transient-below task that becomes invisible.
-            mController.mAtm.getTaskChangeNotificationController().notifyTaskStackChanged();
+            // Notify the change about the transient-below task if entering auto-pip.
+            if (enterAutoPip) {
+                mController.mAtm.getTaskChangeNotificationController().notifyTaskStackChanged();
+            }
             // Prevent spurious background app switches.
             mController.mAtm.stopAppSwitches();
             // The end of transient launch may not reorder task, so make sure to compute the latest
@@ -997,11 +1029,13 @@
                 InsetsControlTarget prevImeTarget = dc.getImeTarget(
                         DisplayContent.IME_TARGET_CONTROL);
                 InsetsControlTarget newImeTarget = null;
+                TaskDisplayArea transientTDA = null;
                 // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
                 // so re-compute in case the IME target is changed after transition.
                 for (int t = 0; t < mTransientLaunches.size(); ++t) {
                     if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
                         newImeTarget = dc.computeImeTarget(true /* updateImeTarget */);
+                        transientTDA = mTransientLaunches.keyAt(i).getTaskDisplayArea();
                         break;
                     }
                 }
@@ -1012,10 +1046,17 @@
                     InputMethodManagerInternal.get().updateImeWindowStatus(
                             false /* disableImeIcon */);
                 }
+                // An uncommitted transient launch can leave incomplete lifecycles if visibilities
+                // didn't change (eg. re-ordering with translucent tasks will leave launcher
+                // in RESUMED state), so force an update here.
+                if (!hasVisibleTransientLaunch && transientTDA != null) {
+                    transientTDA.pauseBackTasks(null /* resuming */);
+                }
             }
             dc.removeImeSurfaceImmediately();
             dc.handleCompleteDeferredRemoval();
         }
+        validateKeyguardOcclusion();
         validateVisibility();
 
         mState = STATE_FINISHED;
@@ -1137,9 +1178,13 @@
         }
         // Check whether the participants were animated from back navigation.
         mController.mAtm.mBackNavigationController.onTransactionReady(this);
+
+        collectOrderChanges();
+
         // Resolve the animating targets from the participants.
         mTargets = calculateTargets(mParticipants, mChanges);
         final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
+        info.setDebugId(mSyncId);
 
         // Repopulate the displays based on the resolved targets.
         mTargetDisplays.clear();
@@ -1172,8 +1217,6 @@
             if (mRecentsDisplayId != INVALID_DISPLAY) break;
         }
 
-        handleNonAppWindowsInTransition(mType, mFlags);
-
         // The callback is only populated for custom activity-level client animations
         sendRemoteCallback(mClientAnimationStartCallback);
 
@@ -1288,6 +1331,27 @@
         info.releaseAnimSurfaces();
     }
 
+    /** Collect tasks which moved-to-top but didn't change otherwise. */
+    @VisibleForTesting
+    void collectOrderChanges() {
+        if (mOnTopTasksStart.isEmpty()) return;
+        final ArrayList<Task> onTopTasksEnd = new ArrayList<>();
+        for (int i = 0; i < mTargetDisplays.size(); ++i) {
+            addOnTopTasks(mTargetDisplays.get(i), onTopTasksEnd);
+        }
+        for (int i = 0; i < onTopTasksEnd.size(); ++i) {
+            final Task task = onTopTasksEnd.get(i);
+            if (mOnTopTasksStart.contains(task)) continue;
+            mParticipants.add(task);
+            int changeIdx = mChanges.indexOfKey(task);
+            if (changeIdx < 0) {
+                mChanges.put(task, new ChangeInfo(task));
+                changeIdx = mChanges.indexOfKey(task);
+            }
+            mChanges.valueAt(changeIdx).mFlags |= ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP;
+        }
+    }
+
     private void postCleanupOnFailure() {
         mController.mAtm.mH.post(() -> {
             synchronized (mController.mAtm.mGlobalLock) {
@@ -1477,19 +1541,6 @@
         }
     }
 
-    private void handleNonAppWindowsInTransition(
-            @TransitionType int transit, @TransitionFlags int flags) {
-        if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
-            // If the occlusion changed but the transition isn't an occlude/unocclude transition,
-            // then we have to notify KeyguardService directly. This can happen if there is
-            // another ongoing transition when the app changes occlusion OR if the app dies or
-            // is killed. Both of these are common during tests.
-            if (transit != TRANSIT_KEYGUARD_OCCLUDE && transit != TRANSIT_KEYGUARD_UNOCCLUDE) {
-                mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange();
-            }
-        }
-    }
-
     private void reportStartReasonsToLogger() {
         // Record transition start in metrics logger. We just assume everything is "DRAWN"
         // at this point since splash-screen is a presentation (shell) detail.
@@ -2182,6 +2233,13 @@
         return mainWin.getAttrs().rotationAnimation;
     }
 
+    private void validateKeyguardOcclusion() {
+        if ((mFlags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
+            mController.mStateValidators.add(
+                mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+        }
+    }
+
     private void validateVisibility() {
         for (int i = mTargets.size() - 1; i >= 0; --i) {
             if (reduceMode(mTargets.get(i).mReadyMode) != TRANSIT_CLOSE) {
@@ -2243,13 +2301,17 @@
          */
         private static final int FLAG_CHANGE_YES_ANIMATION = 0x10;
 
+        /** Whether this change's container moved to the top. */
+        private static final int FLAG_CHANGE_MOVED_TO_TOP = 0x20;
+
         @IntDef(prefix = { "FLAG_" }, value = {
                 FLAG_NONE,
                 FLAG_SEAMLESS_ROTATION,
                 FLAG_TRANSIENT_LAUNCH,
                 FLAG_ABOVE_TRANSIENT_LAUNCH,
                 FLAG_CHANGE_NO_ANIMATION,
-                FLAG_CHANGE_YES_ANIMATION
+                FLAG_CHANGE_YES_ANIMATION,
+                FLAG_CHANGE_MOVED_TO_TOP
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface Flag {}
@@ -2280,7 +2342,7 @@
         int mDisplayId = -1;
         @ActivityInfo.Config int mKnownConfigChanges;
 
-        /** These are just extra info. They aren't used for change-detection. */
+        /** Extra information about this change. */
         @Flag int mFlags = FLAG_NONE;
 
         /** Snapshot surface and luma, if relevant. */
@@ -2332,7 +2394,8 @@
                     || (mWindowingMode != 0 && mContainer.getWindowingMode() != mWindowingMode)
                     || !mContainer.getBounds().equals(mAbsoluteBounds)
                     || mRotation != mContainer.getWindowConfiguration().getRotation()
-                    || mDisplayId != getDisplayId(mContainer);
+                    || mDisplayId != getDisplayId(mContainer)
+                    || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0;
         }
 
         @TransitionInfo.TransitionMode
@@ -2433,6 +2496,9 @@
                     && (mFlags & FLAG_CHANGE_YES_ANIMATION) == 0) {
                 flags |= FLAG_NO_ANIMATION;
             }
+            if ((mFlags & FLAG_CHANGE_MOVED_TO_TOP) != 0) {
+                flags |= FLAG_MOVED_TO_TOP;
+            }
             return flags;
         }
 
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 88c410b..f8edc2b 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -90,8 +90,6 @@
         requireOverlaySurfaceControl();
         mOverlays.add(p);
 
-        mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken());
-
         SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
         t.reparent(p.getSurfaceControl(), mSurfaceControl)
             .show(p.getSurfaceControl());
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bd0344f..4117641 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -157,15 +157,14 @@
     boolean mReparenting;
 
     /**
-     * Map of {@link InsetsState.InternalInsetsType} to the {@link InsetsSourceProvider} that
-     * provides local insets for all children of the current {@link WindowContainer}.
-     *
-     * Note that these InsetsSourceProviders are not part of the {@link InsetsStateController} and
-     * live here. These are supposed to provide insets only to the subtree of the current
+     * Map of the source ID to the {@link InsetsSource} for all children of the current
      * {@link WindowContainer}.
+     *
+     * Note that these sources are not part of the {@link InsetsStateController} and live here.
+     * These are supposed to provide insets only to the subtree of this {@link WindowContainer}.
      */
     @Nullable
-    SparseArray<InsetsSourceProvider> mLocalInsetsSourceProviders = null;
+    SparseArray<InsetsSource> mLocalInsetsSources = null;
 
     @Nullable
     protected InsetsSourceProvider mControllableInsetProvider;
@@ -374,49 +373,46 @@
      * {@link WindowState}s below it.
      *
      * {@link WindowState#mMergedLocalInsetsSources} is updated by considering
-     * {@link WindowContainer#mLocalInsetsSourceProviders} provided by all the parents of the
-     * window.
-     * A given insetsType can be provided as a LocalInsetsSourceProvider only once in a
-     * Parent-to-leaf path.
+     * {@link WindowContainer#mLocalInsetsSources} provided by all the parents of the window.
      *
      * Examples: Please take a look at
      * {@link WindowContainerTests#testAddLocalInsetsSourceProvider()}
-     * {@link
-     * WindowContainerTests#testAddLocalInsetsSourceProvider_windowSkippedIfProvidingOnParent()}
      * {@link WindowContainerTests#testRemoveLocalInsetsSourceProvider()}.
      *
-     * @param aboveInsetsState The InsetsState of all the Windows above the current container.
-     * @param localInsetsSourceProvidersFromParent The local InsetsSourceProviders provided by all
-     *                                             the parents in the hierarchy of the current
-     *                                             container.
-     * @param insetsChangedWindows The windows which the insets changed have changed for.
+     * @param aboveInsetsState             The InsetsState of all the Windows above the current
+     *                                     container.
+     * @param localInsetsSourcesFromParent The local InsetsSourceProviders provided by all
+     *                                     the parents in the hierarchy of the current
+     *                                     container.
+     * @param insetsChangedWindows         The windows which the insets changed have changed for.
      */
     void updateAboveInsetsState(InsetsState aboveInsetsState,
-            SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+            SparseArray<InsetsSource> localInsetsSourcesFromParent,
             ArraySet<WindowState> insetsChangedWindows) {
-        SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
-                localInsetsSourceProvidersFromParent;
-        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
-            mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
-            for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
-                mergedLocalInsetsSourceProviders.put(
-                        mLocalInsetsSourceProviders.keyAt(i),
-                        mLocalInsetsSourceProviders.valueAt(i));
-            }
-        }
+        final SparseArray<InsetsSource> mergedLocalInsetsSources =
+                createMergedSparseArray(localInsetsSourcesFromParent, mLocalInsetsSources);
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            mChildren.get(i).updateAboveInsetsState(aboveInsetsState,
-                    mergedLocalInsetsSourceProviders, insetsChangedWindows);
+            mChildren.get(i).updateAboveInsetsState(aboveInsetsState, mergedLocalInsetsSources,
+                    insetsChangedWindows);
         }
     }
 
-    static <T> SparseArray<T> createShallowCopy(SparseArray<T> inputArray) {
-        SparseArray<T> copyOfInput = new SparseArray<>(inputArray.size());
-        for (int i = 0; i < inputArray.size(); i++) {
-            copyOfInput.append(inputArray.keyAt(i), inputArray.valueAt(i));
+    static <T> SparseArray<T> createMergedSparseArray(SparseArray<T> sa1, SparseArray<T> sa2) {
+        final int size1 = sa1 != null ? sa1.size() : 0;
+        final int size2 = sa2 != null ? sa2.size() : 0;
+        final SparseArray<T> mergedArray = new SparseArray<>(size1 + size2);
+        if (size1 > 0) {
+            for (int i = 0; i < size1; i++) {
+                mergedArray.append(sa1.keyAt(i), sa1.valueAt(i));
+            }
         }
-        return copyOfInput;
+        if (size2 > 0) {
+            for (int i = 0; i < size2; i++) {
+                mergedArray.put(sa2.keyAt(i), sa2.valueAt(i));
+            }
+        }
+        return mergedArray;
     }
 
     /**
@@ -433,25 +429,23 @@
             // This is possible this container is detached when WM shell is responding to a previous
             // request. WM shell will be updated when this container is attached again and the
             // insets need to be updated.
-            Slog.w(TAG, "Can't add local rect insets source provider when detached. " + this);
+            Slog.w(TAG, "Can't add insets frame provider when detached. " + this);
             return;
         }
-        if (mLocalInsetsSourceProviders == null) {
-            mLocalInsetsSourceProviders = new SparseArray<>();
+        if (mLocalInsetsSources == null) {
+            mLocalInsetsSources = new SparseArray<>();
         }
         final int id = InsetsSource.createId(
                 provider.getOwner(), provider.getIndex(), provider.getType());
-        if (mLocalInsetsSourceProviders.get(id) != null) {
+        if (mLocalInsetsSources.get(id) != null) {
             if (DEBUG) {
-                Slog.d(TAG, "The local insets provider for this " + provider
-                        + " already exists. Overwriting");
+                Slog.d(TAG, "The local insets source for this " + provider
+                        + " already exists. Overwriting.");
             }
         }
-        final RectInsetsSourceProvider insetsSourceProvider = new RectInsetsSourceProvider(
-                new InsetsSource(id, provider.getType()),
-                mDisplayContent.getInsetsStateController(), mDisplayContent);
-        mLocalInsetsSourceProviders.put(id, insetsSourceProvider);
-        insetsSourceProvider.setRect(provider.getArbitraryRectangle());
+        final InsetsSource source = new InsetsSource(id, provider.getType());
+        source.setFrame(provider.getArbitraryRectangle());
+        mLocalInsetsSources.put(id, source);
         mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
     }
 
@@ -459,20 +453,19 @@
         if (provider == null) {
             throw new IllegalArgumentException("Insets type not specified.");
         }
-        if (mLocalInsetsSourceProviders == null) {
+        if (mLocalInsetsSources == null) {
             return;
         }
 
         final int id = InsetsSource.createId(
                 provider.getOwner(), provider.getIndex(), provider.getType());
-        if (mLocalInsetsSourceProviders.get(id) == null) {
+        if (mLocalInsetsSources.get(id) == null) {
             if (DEBUG) {
-                Slog.d(TAG, "Given " + provider
-                        + " doesn't have a local insetsSourceProvider.");
+                Slog.d(TAG, "Given " + provider + " doesn't have a local insets source.");
             }
             return;
         }
-        mLocalInsetsSourceProviders.remove(id);
+        mLocalInsetsSources.remove(id);
 
         // Update insets if this window is attached.
         if (mDisplayContent != null) {
@@ -1014,8 +1007,8 @@
         if (dc != null && dc != this) {
             dc.getPendingTransaction().merge(mPendingTransaction);
         }
-        if (dc != this && mLocalInsetsSourceProviders != null) {
-            mLocalInsetsSourceProviders.clear();
+        if (dc != this && mLocalInsetsSources != null) {
+            mLocalInsetsSources.clear();
         }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer child = mChildren.get(i);
@@ -3555,11 +3548,11 @@
             pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
             pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
         }
-        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
-            pw.println(prefix + mLocalInsetsSourceProviders.size() + " LocalInsetsSourceProviders");
+        if (mLocalInsetsSources != null && mLocalInsetsSources.size() != 0) {
+            pw.println(prefix + mLocalInsetsSources.size() + " LocalInsetsSources");
             final String childPrefix = prefix + "  ";
-            for (int i = 0; i < mLocalInsetsSourceProviders.size(); ++i) {
-                mLocalInsetsSourceProviders.valueAt(i).dump(pw, childPrefix);
+            for (int i = 0; i < mLocalInsetsSources.size(); ++i) {
+                mLocalInsetsSources.valueAt(i).dump(childPrefix, pw);
             }
         }
     }
@@ -4129,7 +4122,7 @@
         }
 
         private void hideInsetSourceViewOverflows() {
-            final SparseArray<WindowContainerInsetsSourceProvider> providers =
+            final SparseArray<InsetsSourceProvider> providers =
                     getDisplayContent().getInsetsStateController().getSourceProviders();
             for (int i = providers.size(); i >= 0; i--) {
                 final InsetsSourceProvider insetProvider = providers.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
deleted file mode 100644
index aa2e8f5..0000000
--- a/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.view.InsetsSource;
-
-/**
- * Controller for a specific inset source on the server. It's called provider as it provides the
- * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
- */
-class WindowContainerInsetsSourceProvider extends InsetsSourceProvider {
-    // TODO(b/218734524): Move the window container specific stuff from InsetsSourceProvider to
-    //  this class.
-
-    WindowContainerInsetsSourceProvider(InsetsSource source,
-            InsetsStateController stateController, DisplayContent displayContent) {
-        super(source, stateController, displayContent);
-    }
-}
-
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a7a9060..dde87b1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8624,7 +8624,8 @@
             EmbeddedWindowController.EmbeddedWindow win =
                     new EmbeddedWindowController.EmbeddedWindow(session, this, window,
                             mInputToWindowMap.get(hostInputToken), callingUid, callingPid,
-                            sanitizedType, displayId, focusGrantToken, inputHandleName);
+                            sanitizedType, displayId, focusGrantToken, inputHandleName,
+                            (flags & FLAG_NOT_FOCUSABLE) == 0);
             clientChannel = win.openInputChannel();
             mEmbeddedWindowController.add(clientChannel.getToken(), win);
             applicationHandle = win.getApplicationHandle();
@@ -8745,6 +8746,7 @@
             }
             name = win.toString();
             applicationHandle = win.getApplicationHandle();
+            win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0);
         }
 
         updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
@@ -9022,24 +9024,23 @@
                 Slog.e(TAG, "Embedded window does not belong to the host");
                 return;
             }
-            SurfaceControl.Transaction t = mTransactionFactory.get();
             if (grantFocus) {
-                t.requestFocusTransfer(embeddedWindow.getInputChannelToken(), embeddedWindow.toString(),
-                        hostWindow.mInputChannel.getToken(),
-                        hostWindow.getName(),
-                        hostWindow.getDisplayId()).apply();
+                hostWindow.mInputWindowHandle.setFocusTransferTarget(
+                        embeddedWindow.getInputChannelToken());
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + embeddedWindow,
                         "reason=grantEmbeddedWindowFocus(true)");
             } else {
-                t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(),
-                        embeddedWindow.getInputChannelToken(),
-                        embeddedWindow.toString(),
-                        hostWindow.getDisplayId()).apply();
+                hostWindow.mInputWindowHandle.setFocusTransferTarget(null);
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + hostWindow,
                         "reason=grantEmbeddedWindowFocus(false)");
             }
+            DisplayContent dc = mRoot.getDisplayContent(hostWindow.getDisplayId());
+            if (dc != null) {
+                dc.getInputMonitor().updateInputWindowsLw(true);
+            }
+
             ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
                     embeddedWindow, grantFocus);
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 232b817..680f605 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4495,20 +4495,10 @@
 
     @Override
     void updateAboveInsetsState(InsetsState aboveInsetsState,
-            SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+            SparseArray<InsetsSource> localInsetsSourcesFromParent,
             ArraySet<WindowState> insetsChangedWindows) {
-        SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
-                localInsetsSourceProvidersFromParent;
-        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
-            mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
-            for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
-                mergedLocalInsetsSourceProviders.put(
-                        mLocalInsetsSourceProviders.keyAt(i),
-                        mLocalInsetsSourceProviders.valueAt(i));
-            }
-        }
-        final SparseArray<InsetsSource> mergedLocalInsetsSourcesFromParent =
-                toInsetsSources(mergedLocalInsetsSourceProviders);
+        final SparseArray<InsetsSource> mergedLocalInsetsSources =
+                createMergedSparseArray(localInsetsSourcesFromParent, mLocalInsetsSources);
 
         // Insets provided by the IME window can effect all the windows below it and hence it needs
         // to be visited in the correct order. Because of which updateAboveInsetsState() can't be
@@ -4519,9 +4509,8 @@
                 insetsChangedWindows.add(w);
             }
 
-            if (!mergedLocalInsetsSourcesFromParent.contentEquals(w.mMergedLocalInsetsSources)) {
-                w.mMergedLocalInsetsSources = createShallowCopy(
-                        mergedLocalInsetsSourcesFromParent);
+            if (!mergedLocalInsetsSources.contentEquals(w.mMergedLocalInsetsSources)) {
+                w.mMergedLocalInsetsSources = mergedLocalInsetsSources;
                 insetsChangedWindows.add(w);
             }
 
@@ -4534,17 +4523,6 @@
         }, true /* traverseTopToBottom */);
     }
 
-    private static SparseArray<InsetsSource> toInsetsSources(
-            SparseArray<InsetsSourceProvider> insetsSourceProviders) {
-        final SparseArray<InsetsSource> insetsSources = new SparseArray<>(
-                insetsSourceProviders.size());
-        for (int i = 0; i < insetsSourceProviders.size(); i++) {
-            insetsSources.append(insetsSourceProviders.keyAt(i),
-                    insetsSourceProviders.valueAt(i).getSource());
-        }
-        return insetsSources;
-    }
-
     private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
         // We want to consume the positive sublayer children first because they need to appear
         // above the parent, then this window (the parent), and then the negative sublayer children
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 71acbb4..d64b5a1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -136,7 +136,6 @@
     jmethodID getContextForDisplay;
     jmethodID notifyDropWindow;
     jmethodID getParentSurfaceForPointers;
-    jmethodID isPerDisplayTouchModeEnabled;
 } gServiceClassInfo;
 
 static struct {
@@ -369,10 +368,6 @@
     virtual PointerIconStyle getCustomPointerIconId();
     virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position);
 
-    /* --- If touch mode is enabled per display or global --- */
-
-    virtual bool isPerDisplayTouchModeEnabled();
-
 private:
     sp<InputManagerInterface> mInputManager;
 
@@ -1645,16 +1640,6 @@
             InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
 }
 
-bool NativeInputManager::isPerDisplayTouchModeEnabled() {
-    JNIEnv* env = jniEnv();
-    jboolean enabled =
-            env->CallBooleanMethod(mServiceObj, gServiceClassInfo.isPerDisplayTouchModeEnabled);
-    if (checkAndClearExceptionFromCallback(env, "isPerDisplayTouchModeEnabled")) {
-        return false;
-    }
-    return static_cast<bool>(enabled);
-}
-
 FloatPoint NativeInputManager::getMouseCursorPosition() {
     std::scoped_lock _l(mLock);
     const auto pc = mLocked.pointerController.lock();
@@ -2846,9 +2831,6 @@
     GET_METHOD_ID(gServiceClassInfo.getParentSurfaceForPointers, clazz,
                   "getParentSurfaceForPointers", "(I)J");
 
-    GET_METHOD_ID(gServiceClassInfo.isPerDisplayTouchModeEnabled, clazz,
-                  "isPerDisplayTouchModeEnabled", "()Z");
-
     // InputDevice
 
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
index c7a1af7..b21489a 100644
--- a/services/core/jni/gnss/AGnssRil.cpp
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -89,6 +89,10 @@
 }
 
 jboolean AGnssRil::injectNiSuplMessageData(const jbyteArray& msgData, jint length, jint slotIndex) {
+    if (mIAGnssRil->getInterfaceVersion() <= 2) {
+        ALOGE("IAGnssRil does not support injectNiSuplMessageData().");
+        return JNI_FALSE;
+    }
     JNIEnv* env = getJniEnv();
     jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(msgData, 0));
     auto status = mIAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>((const uint8_t*)bytes,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
deleted file mode 100644
index 111cabd..0000000
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
+++ /dev/null
@@ -1,39 +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.server.inputmethod;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase {
-    @Test
-    public void testSwitchToNextKeyboardLayout() {
-        ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController);
-        InputMethodManagerInternal.get().switchKeyboardLayout(1);
-        verify(mInputMethodManagerService.mSwitchingController)
-                .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any());
-    }
-}
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 c68db4f..4a606ab 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -20,6 +20,7 @@
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ALARM;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_FOREGROUND;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERACTIVE;
@@ -1141,7 +1142,7 @@
             mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
             mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
         }
-        mImpl.waitForIdle(null);
+        mImpl.waitForIdle(LOG_WRITER_INFO);
 
         // Verify that there is only one delivery event reported since one of the broadcasts
         // should have been skipped.
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 17a0b6f..73f645a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
 import static android.os.UserHandle.USER_SYSTEM;
 
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.BroadcastProcessQueue.reasonToString;
 import static com.android.server.am.BroadcastRecord.deliveryStateToString;
 import static com.android.server.am.BroadcastRecord.isReceiverEquals;
@@ -224,7 +225,7 @@
         realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
         realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
         realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
-        realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
+        realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
         realAms.mPackageManagerInt = mPackageManagerInt;
         realAms.mUsageStatsService = mUsageStatsManagerInt;
         realAms.mProcessesReady = true;
@@ -673,7 +674,7 @@
     }
 
     private void waitForIdle() throws Exception {
-        mQueue.waitForIdle(null);
+        mQueue.waitForIdle(LOG_WRITER_INFO);
     }
 
     private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception {
@@ -787,9 +788,6 @@
         mQueue.dumpToDropBoxLocked(TAG);
 
         BroadcastQueue.logv(TAG);
-        BroadcastQueue.logv(TAG, null);
-        BroadcastQueue.logv(TAG, new PrintWriter(new ByteArrayOutputStream()));
-
         BroadcastQueue.logw(TAG);
 
         assertNotNull(mQueue.toString());
@@ -965,7 +963,7 @@
                 // cold-started apps to be thawed, but the modern stack does
             } else {
                 // Confirm that app was thawed
-                verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce()).unfreezeTemporarily(
+                verify(mAms.mOomAdjuster, atLeastOnce()).unfreezeTemporarily(
                         eq(receiverApp), eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
 
                 // Confirm that we added package to process
@@ -1408,7 +1406,7 @@
                 anyInt(), any());
 
         // Finally, verify that we thawed the final receiver
-        verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(callerApp),
+        verify(mAms.mOomAdjuster).unfreezeTemporarily(eq(callerApp),
                 eq(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER));
     }
 
@@ -1903,10 +1901,10 @@
             assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
         }
 
-        mQueue.waitForBarrier(null);
+        mQueue.waitForBarrier(LOG_WRITER_INFO);
         assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
 
-        mQueue.waitForIdle(null);
+        mQueue.waitForIdle(LOG_WRITER_INFO);
         assertTrue(mQueue.isIdleLocked());
         assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
         assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java
new file mode 100644
index 0000000..6eedeea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HardwareKeyboardShortcutControllerTest {
+
+    @Test
+    public void testForwardRotation() {
+        final List<String> handles = Arrays.asList("0", "1", "2", "3");
+        assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", true));
+        assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "2", true));
+        assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", true));
+    }
+
+    @Test
+    public void testBackwardRotation() {
+        final List<String> handles = Arrays.asList("0", "1", "2", "3");
+        assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", false));
+        assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "0", false));
+        assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", false));
+    }
+
+    @Test
+    public void testNotMatching() {
+        final List<String> handles = Arrays.asList("0", "1", "2", "3");
+        assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", true));
+        assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", false));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 397d7b5..7cf5bc8 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -26,8 +26,9 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.util.IntArray;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,10 +58,24 @@
     private static final int TEST_UID_4 = 56926423;
     private static final int TEST_UID_5 = 76421423;
 
+    private static final int TEST_PROC_STATE_1 = 72331;
+    private static final int TEST_PROC_STATE_2 = 792351;
+    private static final int TEST_PROC_STATE_3 = 138831;
+    private static final int TEST_PROC_STATE_4 = 23231;
+    private static final int TEST_PROC_STATE_5 = 42;
+
     private static final Context sContext = InstrumentationRegistry.getTargetContext();
     private final Handler mHandler = Mockito.mock(Handler.class);
     private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
 
+    private void populateDefaultProcStates(CpuWakeupStats obj) {
+        obj.mUidProcStates.put(TEST_UID_1, TEST_PROC_STATE_1);
+        obj.mUidProcStates.put(TEST_UID_2, TEST_PROC_STATE_2);
+        obj.mUidProcStates.put(TEST_UID_3, TEST_PROC_STATE_3);
+        obj.mUidProcStates.put(TEST_UID_4, TEST_PROC_STATE_4);
+        obj.mUidProcStates.put(TEST_UID_5, TEST_PROC_STATE_5);
+    }
+
     @Test
     public void removesOldWakeups() {
         // The xml resource doesn't matter for this test.
@@ -96,6 +111,8 @@
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 12423121;
 
+        populateDefaultProcStates(obj);
+
         obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
 
         // Outside the window, so should be ignored.
@@ -106,15 +123,20 @@
         // Should be attributed
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(1);
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_4)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
     }
 
     @Test
@@ -122,6 +144,8 @@
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 12423121;
 
+        populateDefaultProcStates(obj);
+
         obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_WIFI_IRQ);
 
         // Outside the window, so should be ignored.
@@ -132,15 +156,17 @@
         // Should be attributed
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(1);
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_1)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(
+                TEST_PROC_STATE_4);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
     }
 
     @Test
@@ -148,6 +174,8 @@
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 92123210;
 
+        populateDefaultProcStates(obj);
+
         obj.noteWakeupTimeAndReason(wakeupTime, 4,
                 KERNEL_REASON_WIFI_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
 
@@ -173,23 +201,31 @@
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2,
                 TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(2);
 
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(
+                TEST_PROC_STATE_4);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
 
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(
+                TEST_PROC_STATE_1);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(
+                TEST_PROC_STATE_2);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_4)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
     }
 
     @Test
@@ -206,12 +242,12 @@
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4,
                 TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(1);
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
-        final SparseBooleanArray uids = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
-        assertThat(uids == null || uids.size() == 0).isTrue();
+        final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
+        assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue();
     }
 
     @Test
@@ -259,4 +295,39 @@
         // Any nearby activity should not end up in the attribution map.
         assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
     }
+
+    @Test
+    public void uidProcStateBookkeeping() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+
+        assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+
+        final IntArray uids = new IntArray(87);
+        for (int i = 0; i < 87; i++) {
+            final int uid = mRandom.nextInt(1 << 20);
+            if (uids.indexOf(uid) < 0) {
+                uids.add(uid);
+            }
+        }
+
+        for (int i = 0; i < uids.size(); i++) {
+            final int uid = uids.get(i);
+            for (int j = 0; j < 43; j++) {
+                final int procState = mRandom.nextInt(1 << 15);
+                obj.noteUidProcessState(uid, procState);
+                assertThat(obj.mUidProcStates.get(uid)).isEqualTo(procState);
+            }
+            assertThat(obj.mUidProcStates.size()).isEqualTo(i + 1);
+        }
+
+        for (int i = 0; i < uids.size(); i++) {
+            obj.onUidRemoved(uids.get(i));
+            assertThat(obj.mUidProcStates.indexOfKey(uids.get(i))).isLessThan(0);
+        }
+
+        assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+
+        obj.onUidRemoved(213);
+        assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 42d1ace..37dd9f5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1638,12 +1638,6 @@
                 any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
         mContext.getTestablePermissions().setPermission(
                 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
-                "true",
-                false);
-        Thread.sleep(300);
 
         final String tag = "testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed";
 
@@ -1665,38 +1659,6 @@
     }
 
     @Test
-    public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
-        when(mAmi.applyForegroundServiceNotification(
-                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
-        mContext.getTestablePermissions().setPermission(
-                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
-                "false",
-                false);
-        Thread.sleep(300);
-
-        final String tag = "testEnqueueNotificationWithTag_FGSaddsNoClear";
-
-        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setFlag(FLAG_FOREGROUND_SERVICE, true)
-                .build();
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                n, UserHandle.getUserHandleForUid(mUid), null, 0);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
-                sbn.getId(), sbn.getNotification(), sbn.getUserId());
-        waitForIdle();
-
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(PKG);
-        assertThat(notifs[0].getNotification().flags).isEqualTo(
-                FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR | FLAG_ONGOING_EVENT);
-    }
-
-    @Test
     public void testEnqueueNotificationWithTag_nullAction_fixed() throws Exception {
         Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
                 .setContentTitle("foo")
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
new file mode 100644
index 0000000..ff2ce15
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.soundtrigger;
+
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
+import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
+import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.soundtrigger.ConversionUtil;
+import android.hardware.soundtrigger.SoundTrigger;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+public class ConversionUtilTest {
+    private static final String TAG = "ConversionUtilTest";
+
+    @Test
+    public void testDefaultAudioFormatConstruction() {
+        // This method should generate a real format when passed null
+        final var format = aidl2apiAudioFormatWithDefault(
+                null /** exercise default **/,
+                true /** isInput **/
+                );
+        assertNotNull(format);
+    }
+
+    @Test
+    public void testRecognitionConfigRoundTrip() {
+        final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
+                | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
+        final var data = new byte[] {0x11, 0x22};
+        final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
+        keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
+                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
+                    new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
+                                           new ConfidenceLevel(5000, 80)});
+        keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
+                RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
+                    new ConfidenceLevel(7777, 30),
+                    new ConfidenceLevel(2222, 60)});
+
+        var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
+                keyphrases, data, flags);
+        assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
+    }
+
+    @Test
+    public void testByteArraySharedMemRoundTrip() {
+        final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
+                (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
+                    10000000));
+
+    }
+
+    @Test
+    public void testPhraseRoundTrip() {
+        final var users = new int[] {10001, 10002};
+        final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
+                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
+                Locale.forLanguageTag("no_NO"),
+                "Hello Android", /** keyphrase **/
+                users);
+        assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
+    }
+}
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
similarity index 89%
rename from tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
index f49d9c9..e6a1be8 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.hardware.soundtrigger;
+package com.android.server.soundtrigger;
 
 import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
 import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
@@ -22,6 +22,7 @@
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger;
 import android.media.AudioFormat;
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
@@ -50,10 +51,7 @@
         Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(keyphrase.getId(), unparceled.getId());
-        assertNull(unparceled.getUsers());
-        assertEquals(keyphrase.getLocale(), unparceled.getLocale());
-        assertEquals(keyphrase.getText(), unparceled.getText());
+        assertEquals(keyphrase, unparceled);
     }
 
     @SmallTest
@@ -115,10 +113,7 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.getUuid(), unparceled.getUuid());
-        assertNull(unparceled.getData());
-        assertEquals(ksm.getType(), unparceled.getType());
-        assertTrue(Arrays.equals(keyphrases, unparceled.getKeyphrases()));
+        assertEquals(ksm, unparceled);
     }
 
     @SmallTest
@@ -162,10 +157,7 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.getUuid(), unparceled.getUuid());
-        assertEquals(ksm.getType(), unparceled.getType());
-        assertNull(unparceled.getKeyphrases());
-        assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
+        assertEquals(ksm, unparceled);
     }
 
     @SmallTest
@@ -226,7 +218,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 null /* data */,
                 12345678 /* halEventReceivedMillis */);
 
@@ -251,7 +247,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 new byte[1] /* data */,
                 12345678 /* halEventReceivedMillis */);
 
@@ -278,7 +278,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 data,
                 12345678 /* halEventReceivedMillis */);
 
@@ -335,7 +339,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 null /* data */,
                 null /* keyphraseExtras */,
                 12345678 /* halEventReceivedMillis */);
@@ -364,7 +372,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 new byte[1] /* data */,
                 kpExtra,
                 12345678 /* halEventReceivedMillis */);
@@ -409,7 +421,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 data,
                 kpExtra,
                 12345678 /* halEventReceivedMillis */);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
index 5661b12..7b7a0a3 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
@@ -16,36 +16,18 @@
 
 package com.android.server.soundtrigger_middleware;
 
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
-import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
-import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
-import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
-import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
-import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
-
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 
 import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.SoundTrigger;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Locale;
-
 @RunWith(AndroidJUnit4.class)
 public class ConversionUtilTest {
-    private static final String TAG = "ConversionUtilTest";
+    private static final String TAG = "SoundTriggerMiddlewareConversionUtilTest";
 
     @Test
     public void testUuidRoundTrip() {
@@ -62,54 +44,4 @@
         Uuid reconstructed = ConversionUtil.aidl2hidlUuid(aidl);
         assertEquals(hidl, reconstructed);
     }
-
-    @Test
-    public void testDefaultAudioFormatConstruction() {
-        // This method should generate a real format when passed null
-        final var format = aidl2apiAudioFormatWithDefault(
-                null /** exercise default **/,
-                true /** isInput **/
-                );
-        assertNotNull(format);
-    }
-
-    @Test
-    public void testRecognitionConfigRoundTrip() {
-        final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
-                | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
-        final var data = new byte[] {0x11, 0x22};
-        final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
-        keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
-                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
-                    new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
-                                           new ConfidenceLevel(5000, 80)});
-        keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
-                RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
-                    new ConfidenceLevel(7777, 30),
-                    new ConfidenceLevel(2222, 60)});
-
-        var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
-                keyphrases, data, flags);
-        assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
-    }
-
-    @Test
-    public void testByteArraySharedMemRoundTrip() {
-        final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
-                (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
-        assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
-                    10000000));
-
-    }
-
-    @Test
-    public void testPhraseRoundTrip() {
-        final var users = new int[] {10001, 10002};
-        final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
-                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
-                Locale.forLanguageTag("no_NO"),
-                "Hello Android", /** keyphrase **/
-                users);
-        assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 20d410c..2914de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -81,6 +81,17 @@
         return win;
     }
 
+    private WindowState createDreamWindow() {
+        final WindowState win = createDreamWindow(null, TYPE_BASE_APPLICATION, "dream");
+        final WindowManager.LayoutParams attrs = win.mAttrs;
+        attrs.width = MATCH_PARENT;
+        attrs.height = MATCH_PARENT;
+        attrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        attrs.format = PixelFormat.OPAQUE;
+        return win;
+    }
+
     private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
         final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog"));
         final WindowManager.LayoutParams attrs = win.mAttrs;
@@ -384,4 +395,25 @@
         displayPolicy.requestTransientBars(mNavBarWindow, true);
         assertTrue(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
     }
+
+    @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
+    @Test
+    public void testTransientBarsSuppressedOnDreams() {
+        final WindowState win = createDreamWindow();
+
+        ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;
+        win.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+        win.setRequestedVisibleTypes(0, navigationBars());
+
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+        final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
+        navBarProvider.updateControlForTarget(win, false);
+        navBarProvider.getSource().setVisible(false);
+
+        displayPolicy.setCanSystemBarsBeShownByUser(true);
+        displayPolicy.requestTransientBars(mNavBarWindow, true);
+
+        assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
similarity index 96%
rename from services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index ef20f2b..b35eceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -42,20 +42,20 @@
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class WindowContainerInsetsSourceProviderTest extends WindowTestsBase {
+public class InsetsSourceProviderTest extends WindowTestsBase {
 
     private InsetsSource mSource = new InsetsSource(
             InsetsSource.createId(null, 0, statusBars()), statusBars());
-    private WindowContainerInsetsSourceProvider mProvider;
+    private InsetsSourceProvider mProvider;
     private InsetsSource mImeSource = new InsetsSource(ID_IME, ime());
-    private WindowContainerInsetsSourceProvider mImeProvider;
+    private InsetsSourceProvider mImeProvider;
 
     @Before
     public void setUp() throws Exception {
         mSource.setVisible(true);
-        mProvider = new WindowContainerInsetsSourceProvider(mSource,
+        mProvider = new InsetsSourceProvider(mSource,
                 mDisplayContent.getInsetsStateController(), mDisplayContent);
-        mImeProvider = new WindowContainerInsetsSourceProvider(mImeSource,
+        mImeProvider = new InsetsSourceProvider(mImeSource,
                 mDisplayContent.getInsetsStateController(), mDisplayContent);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 74fde65..ff2944a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -287,7 +287,7 @@
         // IME cannot be the IME target.
         ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
 
-        WindowContainerInsetsSourceProvider statusBarProvider =
+        InsetsSourceProvider statusBarProvider =
                 getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars());
         final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> imeOverrideProviders =
                 new SparseArray<>();
@@ -353,7 +353,7 @@
     public void testTransientVisibilityOfFixedRotationState() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final WindowContainerInsetsSourceProvider provider = getController()
+        final InsetsSourceProvider provider = getController()
                 .getOrCreateSourceProvider(ID_STATUS_BAR, statusBars());
         provider.setWindowContainer(statusBar, null, null);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index d7bf4b0..90506d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1885,6 +1885,39 @@
         assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
     }
 
+    @Test
+    public void testMoveToTopWhileVisible() {
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
+        final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+        final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+        // Start with taskB on top and taskA on bottom but both visible.
+        final Task rootTaskA = createTask(mDisplayContent);
+        final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */);
+        final Task taskB = createTask(mDisplayContent);
+        leafTaskA.setVisibleRequested(true);
+        taskB.setVisibleRequested(true);
+        // manually collect since this is a test transition and not known by transitionController.
+        transition.collect(leafTaskA);
+        rootTaskA.moveToFront("test", leafTaskA);
+
+        // All the tasks were already visible, so there shouldn't be any changes
+        ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
+                participants, changes);
+        assertTrue(targets.isEmpty());
+
+        // After collecting order changes, it should recognize that a task moved to top.
+        transition.collectOrderChanges();
+        targets = Transition.calculateTargets(participants, changes);
+        assertEquals(1, targets.size());
+
+        // Make sure the flag is set
+        final TransitionInfo info = Transition.calculateTransitionInfo(
+                transition.mType, 0 /* flags */, targets, mMockT);
+        assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
+        assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
+    }
+
     private static void makeTaskOrganized(Task... tasks) {
         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
         for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 6261e56..a1ddd57 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -484,7 +484,7 @@
                 windowState.mSurfaceAnimator).getAnimationType();
         assertTrue(parent.isAnimating(CHILDREN));
 
-        windowState.setControllableInsetProvider(mock(WindowContainerInsetsSourceProvider.class));
+        windowState.setControllableInsetProvider(mock(InsetsSourceProvider.class));
         assertFalse(parent.isAnimating(CHILDREN));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 373f994..d19c996 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -801,8 +801,8 @@
                 new Rect(0, 0, 1080, 200));
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
 
-        assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSourceProviders
-                .valueAt(0).getSource().getType()).isEqualTo(
+        assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources
+                .valueAt(0).getType()).isEqualTo(
                         WindowInsets.Type.systemOverlays());
     }
 
@@ -831,7 +831,7 @@
                 WindowInsets.Type.systemOverlays());
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2);
 
-        assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSourceProviders.size()).isEqualTo(0);
+        assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources.size()).isEqualTo(0);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 0d7cdc8..7e3ec55 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -467,6 +468,12 @@
         return createWindow(null, type, activity, name);
     }
 
+    WindowState createDreamWindow(WindowState parent, int type, String name) {
+        final WindowToken token = createWindowToken(
+                mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, type);
+        return createWindow(parent, type, token, name);
+    }
+
     // TODO: Move these calls to a builder?
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
             IWindow iwindow) {
diff --git a/services/voiceinteraction/OWNERS b/services/voiceinteraction/OWNERS
index ef1061b..40e8d26 100644
--- a/services/voiceinteraction/OWNERS
+++ b/services/voiceinteraction/OWNERS
@@ -1 +1,2 @@
 include /core/java/android/service/voice/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
index 01b2cb9..1e41886 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
index 86c4bbf..37a325e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
@@ -276,16 +276,15 @@
         // for our clients.
         mGlobalEventSession = new IInjectGlobalEvent.Stub() {
             /**
-             * Overrides IInjectGlobalEvent method.
              * Simulate a HAL process restart. This method is not included in regular HAL interface,
              * since the entire process is restarted by sending a signal.
              * Since we run in-proc, we must offer an explicit restart method.
              * oneway
              */
             @Override
-            public void triggerRestart() throws RemoteException {
+            public void triggerRestart() {
                 synchronized (FakeSoundTriggerHal.this.mLock) {
-                    if (mIsDead) throw new DeadObjectException();
+                    if (mIsDead) return;
                     mIsDead = true;
                     mInjectionDispatcher.wrap((ISoundTriggerInjection cb) ->
                             cb.onRestarted(this));
@@ -305,15 +304,15 @@
                 }
             }
 
-            /**
-             * Overrides IInjectGlobalEvent method.
-             * oneway
-             */
+            // oneway
             @Override
             public void setResourceContention(boolean isResourcesContended,
-                        IAcknowledgeEvent callback) throws RemoteException {
+                        IAcknowledgeEvent callback) {
                 synchronized (FakeSoundTriggerHal.this.mLock) {
-                    if (mIsDead) throw new DeadObjectException();
+                    // oneway, so don't throw on death
+                    if (mIsDead || mIsResourceContended == isResourcesContended) {
+                        return;
+                    }
                     mIsResourceContended = isResourcesContended;
                     // Introducing contention is the only injection which can't be
                     // observed by the ST client.
@@ -325,7 +324,19 @@
                     }
                 }
             }
+
+            // oneway
+            @Override
+            public void triggerOnResourcesAvailable() {
+                synchronized (FakeSoundTriggerHal.this.mLock) {
+                    // oneway, so don't throw on death
+                    if (mIsDead) return;
+                    mGlobalCallbackDispatcher.wrap((ISoundTriggerHwGlobalCallback cb) ->
+                            cb.onResourcesAvailable());
+                }
+            }
         };
+
         // Register the global event injection interface
         mInjectionDispatcher.wrap((ISoundTriggerInjection cb)
                 -> cb.registerGlobalEventInjection(mGlobalEventSession));
@@ -465,7 +476,9 @@
             if (session == null) {
                 Slog.wtf(TAG, "Attempted to start recognition with invalid handle");
             }
-
+            if (mIsResourceContended) {
+                throw new ServiceSpecificException(Status.RESOURCE_CONTENTION);
+            }
             if (session.getIsUnloaded()) {
                 // TODO(b/274470274) this is a deficiency in the existing HAL API, there is no way
                 // to handle this race gracefully
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 2f8d17d..4c134af 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -35,7 +35,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -463,7 +463,7 @@
                 printObject(originatorIdentity),
                 printArgs(args),
                 printObject(retVal));
-        Log.i(TAG, message);
+        Slog.i(TAG, message);
         appendMessage(message);
     }
 
@@ -474,7 +474,7 @@
                 object,
                 printObject(originatorIdentity),
                 printArgs(args));
-        Log.i(TAG, message);
+        Slog.i(TAG, message);
         appendMessage(message);
     }
 
@@ -486,7 +486,7 @@
                 object,
                 printObject(originatorIdentity),
                 printArgs(args));
-        Log.e(TAG, message, ex);
+        Slog.e(TAG, message, ex);
         appendMessage(message + " " + ex.toString());
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 48a39e6..f3cb9ba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -74,9 +74,8 @@
 
 import java.io.PrintWriter;
 import java.time.Instant;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -122,8 +121,8 @@
     private static final int DETECTION_SERVICE_TYPE_VISUAL_QUERY = 2;
 
     // TODO: This may need to be a Handler(looper)
-    private final ScheduledExecutorService mScheduledExecutorService =
-            Executors.newSingleThreadScheduledExecutor();
+    private final ScheduledThreadPoolExecutor mScheduledExecutorService =
+            new ScheduledThreadPoolExecutor(1);
     @Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
     private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
     @NonNull private final ServiceConnectionFactory mHotwordDetectionServiceConnectionFactory;
@@ -210,6 +209,7 @@
         if (mReStartPeriodSeconds <= 0) {
             mCancellationTaskFuture = null;
         } else {
+            mScheduledExecutorService.setRemoveOnCancelPolicy(true);
             // TODO: we need to be smarter here, e.g. schedule it a bit more often,
             //  but wait until the current session is closed.
             mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index fa4c9b2..ff378ba 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -43,6 +43,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -325,11 +326,19 @@
     }
 
     private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+        // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+        // list of callbacks to notify is copied over from the original list modified here. I also
+        // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+        // synchronize on outgoing and incoming operations.
         mCallbacks.register(c);
         updateNewCallbackWithState(c);
     }
 
     private void removeRegistrationCallback(IImsRegistrationCallback c) {
+        // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+        // list of callbacks to notify is copied over from the original list modified here. I also
+        // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+        // synchronize on outgoing and incoming operations.
         mCallbacks.unregister(c);
     }
 
@@ -420,7 +429,7 @@
     @SystemApi
     public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onRegistered(attributes);
             } catch (RemoteException e) {
@@ -449,7 +458,7 @@
     @SystemApi
     public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onRegistering(attributes);
             } catch (RemoteException e) {
@@ -507,7 +516,7 @@
         updateToDisconnectedState(info, suggestedAction, imsRadioTech);
         // ImsReasonInfo should never be null.
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onDeregistered(reasonInfo, suggestedAction, imsRadioTech);
             } catch (RemoteException e) {
@@ -569,7 +578,7 @@
         updateToDisconnectedState(info, suggestedAction, imsRadioTech);
         // ImsReasonInfo should never be null.
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onDeregisteredWithDetails(reasonInfo, suggestedAction, imsRadioTech, details);
             } catch (RemoteException e) {
@@ -591,7 +600,7 @@
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
             } catch (RemoteException e) {
@@ -614,7 +623,20 @@
             mUris = ArrayUtils.cloneOrNull(uris);
             mUrisSet = true;
         }
-        mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris));
+        broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris));
+    }
+
+    /**
+     * Broadcast the specified operation in a synchronized manner so that multiple threads do not
+     * try to call broadcast at the same time, which will generate an error.
+     * @param c The Consumer lambda method containing the callback to call.
+     */
+    private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c) {
+        // One broadcast can happen at a time, so synchronize threads so only one
+        // beginBroadcast/endBroadcast happens at a time.
+        synchronized (mCallbacks) {
+            mCallbacks.broadcastAction(c);
+        }
     }
 
     private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) {
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
index 2954c2d..e229f05 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
@@ -18,7 +18,7 @@
 
 import android.telephony.satellite.SatelliteDatagram;
 
-import com.android.internal.telephony.ILongConsumer;
+import com.android.internal.telephony.IVoidConsumer;
 
 /**
  * Interface for satellite datagrams callback.
@@ -31,10 +31,10 @@
      * @param datagramId An id that uniquely identifies incoming datagram.
      * @param datagram Datagram received from satellite.
      * @param pendingCount Number of datagrams yet to be received from satellite.
-     * @param callback This callback will be used by datagram receiver app to send received
-     *                 datagramId to Telephony. If the callback is not received within five minutes,
-     *                 Telephony will resend the datagram.
+     * @param callback This callback will be used by datagram receiver app to to inform
+     *                 Telephony that datagram is received. If the callback is not received
+     *                 within five minutes, Telephony will resend the datagram.
      */
     void onSatelliteDatagramReceived(long datagramId, in SatelliteDatagram datagram,
-            int pendingCount, ILongConsumer callback);
+            int pendingCount, IVoidConsumer callback);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index d8a6faf..d0409bf 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 
-import com.android.internal.telephony.ILongConsumer;
+import java.util.function.Consumer;
 
 /**
  * A callback class for listening to satellite datagrams.
@@ -33,11 +33,11 @@
      * @param datagramId An id that uniquely identifies incoming datagram.
      * @param datagram Datagram to be received over satellite.
      * @param pendingCount Number of datagrams yet to be received by the app.
-     * @param callback This callback will be used by datagram receiver app to send received
-     *                 datagramId to Telephony. If the callback is not received within five minutes,
-     *                 Telephony will resend the datagram.
+     * @param callback This callback will be used by datagram receiver app to inform Telephony
+     *                 that they received the datagram. If the callback is not received within
+     *                 five minutes, Telephony will resend the datagram.
      */
     @UnsupportedAppUsage
     void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
-            int pendingCount, @NonNull ILongConsumer callback);
+            int pendingCount, @NonNull Consumer<Void> callback);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7d82fd8..20f9bc8b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -37,7 +37,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 
 import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.ILongConsumer;
+import com.android.internal.telephony.IVoidConsumer;
 import com.android.internal.telephony.ITelephony;
 import com.android.telephony.Rlog;
 
@@ -862,7 +862,7 @@
      *
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @param provisionData Data from the provisioning app that can be used by provisioning server
      * @param cancellationSignal The optional signal used by the caller to cancel the provision
      *                           request. Even when the cancellation is signaled, Telephony will
      *                           still trigger the callback to return the result of this request.
@@ -874,13 +874,14 @@
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @UnsupportedAppUsage
-    public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
+    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
         Objects.requireNonNull(token);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(provisionData);
 
         ICancellationSignal cancelRemote = null;
         try {
@@ -893,7 +894,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                cancelRemote = telephony.provisionSatelliteService(mSubId, token, regionId,
+                cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
                         errorCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
@@ -1186,10 +1187,22 @@
                             @Override
                             public void onSatelliteDatagramReceived(long datagramId,
                                     @NonNull SatelliteDatagram datagram, int pendingCount,
-                                    @NonNull ILongConsumer ack) {
+                                    @NonNull IVoidConsumer internalAck) {
+                                Consumer<Void> externalAck = new Consumer<Void>() {
+                                    @Override
+                                    public void accept(Void result) {
+                                        try {
+                                            internalAck.accept();
+                                        }  catch (RemoteException e) {
+                                              logd("onSatelliteDatagramReceived "
+                                                      + "RemoteException: " + e);
+                                        }
+                                    }
+                                };
+
                                 executor.execute(() -> Binder.withCleanCallingIdentity(
                                         () -> callback.onSatelliteDatagramReceived(
-                                                datagramId, datagram, pendingCount, ack)));
+                                                datagramId, datagram, pendingCount, externalAck)));
                             }
                         };
                 sSatelliteDatagramCallbackMap.put(callback, internalCallback);
@@ -1244,7 +1257,7 @@
      * This method requests modem to check if there are any pending datagrams to be received over
      * satellite. If there are any incoming datagrams, they will be received via
      * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
-     *        ILongConsumer)}
+     * Consumer)} )}
      *
      * @param executor The executor on which the result listener will be called.
      * @param resultListener Listener for the {@link SatelliteError} result of the operation.
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index a780cb9..ea4e2e2 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -67,6 +67,15 @@
             in IIntegerConsumer resultCallback);
 
     /**
+     * Allow cellular modem scanning while satellite mode is on.
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     * and {@code false} to disable
+     * @param errorCallback The callback to receive the error code result of the operation.
+     */
+    void enableCellularModemWhileSatelliteModeIsOn(in boolean enabled,
+        in IIntegerConsumer errorCallback);
+
+    /**
      * Request to enable or disable the satellite modem and demo mode. If the satellite modem
      * is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
@@ -194,7 +203,7 @@
      *
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @param provisionData Data from the provisioning app that can be used by provisioning server
      * @param resultCallback The callback to receive the error code result of the operation.
      *
      * Valid error codes returned:
@@ -210,7 +219,7 @@
      *   SatelliteError:REQUEST_ABORTED
      *   SatelliteError:NETWORK_TIMEOUT
      */
-    void provisionSatelliteService(in String token, in String regionId,
+    void provisionSatelliteService(in String token, in byte[] provisionData,
             in IIntegerConsumer resultCallback);
 
     /**
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index debb394..d606f87 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -79,6 +79,15 @@
         }
 
         @Override
+        public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+                IIntegerConsumer errorCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .enableCellularModemWhileSatelliteModeIsOn(enabled, errorCallback),
+                    "enableCellularModemWhileSatelliteModeIsOn");
+        }
+
+        @Override
         public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
                 IIntegerConsumer errorCallback) throws RemoteException {
             executeMethodAsync(
@@ -132,11 +141,11 @@
         }
 
         @Override
-        public void provisionSatelliteService(String token, String regionId,
+        public void provisionSatelliteService(String token, byte[] provisionData,
                 IIntegerConsumer errorCallback) throws RemoteException {
             executeMethodAsync(
                     () -> SatelliteImplBase.this
-                            .provisionSatelliteService(token, regionId, errorCallback),
+                            .provisionSatelliteService(token, provisionData, errorCallback),
                     "provisionSatelliteService");
         }
 
@@ -261,6 +270,17 @@
     }
 
     /**
+     * Allow cellular modem scanning while satellite mode is on.
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     * and {@code false} to disable
+     * @param errorCallback The callback to receive the error code result of the operation.
+     */
+    public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+            @NonNull IIntegerConsumer errorCallback) {
+        // stub implementation
+    }
+
+    /**
      * Request to enable or disable the satellite modem and demo mode. If the satellite modem is
      * enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
@@ -401,7 +421,8 @@
      *
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @param provisionData Data from the provisioning app that can be used by provisioning 
+     *                      server
      * @param errorCallback The callback to receive the error code result of the operation.
      *
      * Valid error codes returned:
@@ -417,7 +438,7 @@
      *   SatelliteError:REQUEST_ABORTED
      *   SatelliteError:NETWORK_TIMEOUT
      */
-    public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
+    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
             @NonNull IIntegerConsumer errorCallback) {
         // stub implementation
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bab08b5..cbdf38ae 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2823,15 +2823,15 @@
      * @param subId The subId of the subscription to be provisioned.
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @provisionData Data from the provisioning app that can be used by provisioning server
      * @param callback The callback to get the result of the request.
      *
      * @return The signal transport used by callers to cancel the provision request.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    ICancellationSignal provisionSatelliteService(int subId, in String token, in String regionId,
-            in IIntegerConsumer callback);
+    ICancellationSignal provisionSatelliteService(int subId, in String token,
+            in byte[] provisionData, in IIntegerConsumer callback);
 
     /**
      * Unregister the subscription with the satellite provider.
@@ -2980,4 +2980,13 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
     void requestTimeForNextSatelliteVisibility(int subId, in ResultReceiver receiver);
+
+    /**
+     * This API can be used by only CTS to update satellite vendor service package name.
+     *
+     * @param servicePackageName The package name of the satellite vendor service.
+     * @return {@code true} if the satellite vendor service is set successfully,
+     * {@code false} otherwise.
+     */
+    boolean setSatelliteServicePackageName(in String servicePackageName);
 }
diff --git a/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl b/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl
new file mode 100644
index 0000000..b5557fd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package com.android.internal.telephony;
+
+ /**
+  * Copies consumer pattern for an operation that requires void result from another process to
+  * finish.
+  */
+ oneway interface IVoidConsumer {
+    void accept();
+ }
\ No newline at end of file
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index fef5211..4ba538e 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -50,6 +50,9 @@
         "platform-test-annotations",
         "wm-flicker-window-extensions",
     ],
+    data: [
+        ":FlickerTestApp",
+    ],
 }
 
 java_library {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 9dc4bf0..314b9e4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -280,21 +280,28 @@
  *
  * @param originalLayer
  * ```
+ *
  * Layer that should be visible at the start
+ *
  * @param newLayer Layer that should be visible at the end
  * @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
+ *
  * ```
  *      when checking the transition. If true we will not fail the assertion if a rotation layer is
  *      visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
  *      visible.
  * @param ignoreSnapshot
  * ```
+ *
  * If the snapshot layer should be ignored during the transition
+ *
  * ```
  *     (useful mostly for app launch)
  * @param ignoreSplashscreen
  * ```
+ *
  * If the splashscreen layer should be ignored during the transition.
+ *
  * ```
  *      If true then we will allow for a splashscreen to be shown before the layer is shown,
  *      otherwise we won't and the layer must appear immediately.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
index 7aea05d..fde0981 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
@@ -71,7 +71,7 @@
      * Open Assistance UI.
      *
      * @param longpress open the UI by long pressing power button. Otherwise open the UI through
-     * vioceinteraction shell command directly.
+     *   vioceinteraction shell command directly.
      */
     @JvmOverloads
     fun openUI(longpress: Boolean = false) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
index 79c048a..d4f48fe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -60,7 +60,6 @@
      *
      * @param wmHelper Helper used to get window region.
      * @param direction UiAutomator Direction enum to indicate the swipe direction.
-     *
      * @return true if the swipe operation is successful.
      */
     fun switchToPreviousAppByQuickSwitchGesture(
@@ -96,7 +95,6 @@
      * @param packageName The targe application's package name.
      * @param identifier The resource id of the target object.
      * @param timeout The timeout duration in milliseconds.
-     *
      * @return true if the target object exists.
      */
     @JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
new file mode 100644
index 0000000..1ccac13
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.traces.parsers.toFlickerComponent
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.helpers.SYSTEMUI_PACKAGE
+
+class LetterboxAppHelper
+@JvmOverloads
+constructor(
+    instr: Instrumentation,
+    launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL,
+    component: ComponentNameMatcher =
+        ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
+
+    fun clickRestart(wmHelper: WindowManagerStateHelper) {
+        val restartButton = uiDevice.wait(Until.findObject(By.res(
+            SYSTEMUI_PACKAGE, "size_compat_restart_button")), FIND_TIMEOUT)
+        restartButton?.run { restartButton.click() } ?: error("Restart button not found")
+
+        // size compat mode restart confirmation dialog button
+        val restartDialogButton = uiDevice.wait(Until.findObject(By.res(
+            SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")), FIND_TIMEOUT)
+        restartDialogButton?.run { restartDialogButton.click() }
+            ?: error("Restart dialog button not found")
+        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index e497ae4..a72c12d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -57,18 +57,16 @@
         obj.click()
     }
 
-    /**
-     * Drags the PIP window to the provided final coordinates without releasing the pointer.
-     */
-    fun dragPipWindowAwayFromEdgeWithoutRelease(
-        wmHelper: WindowManagerStateHelper,
-        steps: Int
-    ) {
+    /** Drags the PIP window to the provided final coordinates without releasing the pointer. */
+    fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) {
         val initWindowRect = getWindowRect(wmHelper).clone()
 
         // initial pointer at the center of the window
-        val initialCoord = GestureHelper.Tuple(initWindowRect.centerX().toFloat(),
-                initWindowRect.centerY().toFloat())
+        val initialCoord =
+            GestureHelper.Tuple(
+                initWindowRect.centerX().toFloat(),
+                initWindowRect.centerY().toFloat()
+            )
 
         // the offset to the right (or left) of the window center to drag the window to
         val offset = 50
@@ -76,8 +74,8 @@
         // the actual final x coordinate with the offset included;
         // if the pip window is closer to the right edge of the display the offset is negative
         // otherwise the offset is positive
-        val endX = initWindowRect.centerX() +
-            offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
+        val endX =
+            initWindowRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
         val finalCoord = GestureHelper.Tuple(endX.toFloat(), initWindowRect.centerY().toFloat())
 
         // drag to the final coordinate
@@ -106,7 +104,8 @@
         val startX = initWindowRect.centerX()
         val y = initWindowRect.centerY()
 
-        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+        val displayRect =
+            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
                 ?: throw IllegalStateException("Default display is null")
 
         // the offset to the right (or left) of the display center to drag the window to
@@ -129,7 +128,8 @@
     fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
         val windowRect = getWindowRect(wmHelper)
 
-        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+        val displayRect =
+            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
                 ?: throw IllegalStateException("Default display is null")
 
         return windowRect.centerX() > displayRect.centerX()
@@ -301,9 +301,7 @@
         closePipWindow(WindowManagerStateHelper(mInstrumentation))
     }
 
-    /**
-     * Returns the pip window bounds.
-     */
+    /** Returns the pip window bounds. */
     fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
         val windowRegion = wmHelper.getWindowRegion(this)
         require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
index 432df20..c355e27 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
@@ -39,4 +39,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index a4e4b6f..df9d33b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -40,6 +40,7 @@
  *     Don't show if this is not explicitly requested by the user and the input method
  *     is fullscreen. That would be too disruptive.
  * ```
+ *
  * More details on b/190352379
  *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index e85da1f..7954dd1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -40,6 +40,7 @@
  *     Don't show if this is not explicitly requested by the user and the input method
  *     is fullscreen. That would be too disruptive.
  * ```
+ *
  * More details on b/190352379
  *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 1fee20d..f97a038 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.isShellTransitionsEnabled
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -27,6 +26,7 @@
 import android.tools.device.flicker.legacy.FlickerTestFactory
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp
 import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
index efda0ff..e1aa418 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
@@ -40,4 +40,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index daee332..690ed53 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -20,13 +20,13 @@
 import android.tools.common.NavBar
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.FixMethodOrder
@@ -86,9 +86,7 @@
         }
     }
     /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun entireScreenCovered() = super.entireScreenCovered()
+    @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 7514c9b..866e858 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -19,14 +19,14 @@
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -44,20 +44,27 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] that automatically displays IME and wait animation to complete
  * ```
+ *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index a57aa5b..6f22589 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -29,6 +28,7 @@
 import android.view.WindowInsets.Type.statusBars
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
index cffc05d..8891d26 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
@@ -45,4 +45,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 9ea12a9..7135df6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -20,7 +20,6 @@
 import android.tools.common.datatypes.component.ComponentNameMatcher
 import android.tools.common.traces.ConditionsFactory
 import android.tools.device.flicker.isShellTransitionsEnabled
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -28,6 +27,7 @@
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
 import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
 import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index e8f9aa3..3c577ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -44,6 +44,7 @@
  *     Launch a secondary activity within the app
  *     Close the secondary activity back to the initial one
  * ```
+ *
  * Notes:
  * ```
  *     1. Part of the test setup occurs automatically via
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 05abf9f..360a233 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -39,6 +39,7 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] by clicking it's icon on all apps and wait animation to complete
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 63ffee6..12c0874 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
@@ -135,14 +135,15 @@
         }
 
         /**
-         * Ensures that posted notifications will be visible on the lockscreen and not
-         * suppressed due to being marked as seen.
+         * 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",
-        )
+        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 a221ef6..222caed 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
@@ -150,14 +150,15 @@
         }
 
         /**
-         * Ensures that posted notifications will be visible on the lockscreen and not
-         * suppressed due to being marked as seen.
+         * 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",
-        )
+        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/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
index d90b3ca..43d28fa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
@@ -42,4 +42,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 3fccd12..03f21e4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -221,9 +221,10 @@
                     .getIdentifier("image_wallpaper_component", "string", "android")
             // frameworks/base/core/res/res/values/config.xml returns package plus class name,
             // but wallpaper layer has only class name
-            val rawComponentMatcher = ComponentNameMatcher.unflattenFromString(
-                instrumentation.targetContext.resources.getString(resourceId)
-            )
+            val rawComponentMatcher =
+                ComponentNameMatcher.unflattenFromString(
+                    instrumentation.targetContext.resources.getString(resourceId)
+                )
 
             return ComponentNameMatcher(rawComponentMatcher.className)
         }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 4a4180b..fe789a7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -39,6 +39,7 @@
  *      0 -> 90 degrees
  *      90 -> 0 degrees
  * ```
+ *
  * Actions:
  * ```
  *     Launch an app (via intent)
@@ -47,22 +48,29 @@
  *     Change device orientation
  *     Stop tracing
  * ```
+ *
  * To run this test: `atest FlickerTests:ChangeAppRotationTest`
  *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 17b3b2b..4d010f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -45,6 +45,7 @@
  *      90 -> 0 degrees
  *      90 -> 0 degrees (with starved UI thread)
  * ```
+ *
  * Actions:
  * ```
  *     Launch an app in fullscreen and supporting seamless rotation (via intent)
@@ -53,22 +54,29 @@
  *     Change device orientation
  *     Stop tracing
  * ```
+ *
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 5361d73f..1ec9ec9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -88,6 +88,18 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".NonResizeablePortraitActivity"
+                  android:theme="@style/CutoutNever"
+                  android:resizeableActivity="false"
+                  android:screenOrientation="portrait"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeablePortraitActivity"
+                  android:label="NonResizeablePortraitActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
         <activity android:name=".LaunchNewActivity"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
                   android:theme="@style/CutoutShortEdges"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index b61bc0c..9c3226b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -67,6 +67,12 @@
                 FLICKER_APP_PACKAGE + ".NonResizeableActivity");
     }
 
+    public static class NonResizeablePortraitActivity {
+        public static final String LABEL = "NonResizeablePortraitActivity";
+        public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+                FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
+    }
+
     public static class DialogThemedActivity {
         public static final String LABEL = "DialogThemedActivity";
         public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java
new file mode 100644
index 0000000..4b420dc
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class NonResizeablePortraitActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.activity_non_resizeable);
+    }
+}
diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp
index de86498..5f03aa2 100644
--- a/tests/OdmApps/Android.bp
+++ b/tests/OdmApps/Android.bp
@@ -26,4 +26,7 @@
     srcs: ["src/**/*.java"],
     libs: ["tradefed"],
     test_suites: ["device-tests"],
+    data: [
+        ":TestOdmApp",
+    ],
 }
diff --git a/tests/SoundTriggerTestApp/OWNERS b/tests/SoundTriggerTestApp/OWNERS
index 9db19a3..a0fcfc5 100644
--- a/tests/SoundTriggerTestApp/OWNERS
+++ b/tests/SoundTriggerTestApp/OWNERS
@@ -1,2 +1,2 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
 mdooley@google.com
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
deleted file mode 100644
index cc0fa1c..0000000
--- a/tests/SoundTriggerTests/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
-  LOCAL_SRC_FILES := $(call all-subdir-java-files)
-  LOCAL_PRIVILEGED_MODULE := true
-  LOCAL_CERTIFICATE := platform
-  TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
-else
-  LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
-endif
-
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_PACKAGE_NAME := SoundTriggerTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/SoundTriggerTests/AndroidManifest.xml
deleted file mode 100644
index f7454c7..0000000
--- a/tests/SoundTriggerTests/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.hardware.soundtrigger">
-    <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.hardware.soundtrigger"
-                     android:label="Tests for android.hardware.soundtrigger" />
-</manifest>
diff --git a/tests/SoundTriggerTests/OWNERS b/tests/SoundTriggerTests/OWNERS
deleted file mode 100644
index 816bc6b..0000000
--- a/tests/SoundTriggerTests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /core/java/android/media/soundtrigger/OWNERS
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
deleted file mode 100644
index 2c3592c..0000000
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.soundtrigger;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.media.soundtrigger.SoundTriggerManager;
-import android.os.ParcelUuid;
-import android.os.ServiceManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.app.ISoundTriggerService;
-
-import org.mockito.MockitoAnnotations;
-
-import java.io.DataOutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Random;
-import java.util.UUID;
-
-public class GenericSoundModelTest extends AndroidTestCase {
-    static final int MSG_DETECTION_ERROR = -1;
-    static final int MSG_DETECTION_RESUME = 0;
-    static final int MSG_DETECTION_PAUSE = 1;
-    static final int MSG_KEYPHRASE_TRIGGER = 2;
-    static final int MSG_GENERIC_TRIGGER = 4;
-
-    private Random random = new Random();
-    private HashSet<UUID> loadedModelUuids;
-    private ISoundTriggerService soundTriggerService;
-    private SoundTriggerManager soundTriggerManager;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        MockitoAnnotations.initMocks(this);
-
-        Context context = getContext();
-        soundTriggerService = ISoundTriggerService.Stub.asInterface(
-                ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
-        soundTriggerManager = (SoundTriggerManager) context.getSystemService(
-                Context.SOUND_TRIGGER_SERVICE);
-
-        loadedModelUuids = new HashSet<UUID>();
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        for (UUID modelUuid : loadedModelUuids) {
-            soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
-        }
-        super.tearDown();
-    }
-
-    GenericSoundModel new_sound_model() {
-        // Create sound model
-        byte[] data = new byte[1024];
-        random.nextBytes(data);
-        UUID modelUuid = UUID.randomUUID();
-        UUID mVendorUuid = UUID.randomUUID();
-        return new GenericSoundModel(modelUuid, mVendorUuid, data);
-    }
-
-    @SmallTest
-    public void testUpdateGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        // Update sound model
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-
-        // Confirm it was updated
-        GenericSoundModel returnedModel =
-                soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
-        assertEquals(model, returnedModel);
-    }
-
-    @SmallTest
-    public void testDeleteGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        // Update sound model
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-
-        // Delete sound model
-        soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
-        loadedModelUuids.remove(model.getUuid());
-
-        // Confirm it was deleted
-        GenericSoundModel returnedModel =
-                soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
-        assertEquals(null, returnedModel);
-    }
-
-    @LargeTest
-    public void testStartStopGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        boolean captureTriggerAudio = true;
-        boolean allowMultipleTriggers = true;
-        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                null, null);
-        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-        // Update and start sound model recognition
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-        int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
-                config);
-        assertEquals("Could Not Start Recognition with code: " + r,
-                android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-
-        // Stop recognition
-        r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
-        assertEquals("Could Not Stop Recognition with code: " + r,
-                android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-    }
-
-    @LargeTest
-    public void testTriggerGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        boolean captureTriggerAudio = true;
-        boolean allowMultipleTriggers = true;
-        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                null, null);
-        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-        // Update and start sound model
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-        soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
-
-        // Send trigger to stub HAL
-        Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
-        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
-        out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
-        out.flush();
-        socket.close();
-
-        // Verify trigger was received
-        verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
-    }
-
-    /**
-     * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
-     * recognition. Intended to find unexpected errors that occur in unexpected states.
-     */
-    @LargeTest
-    public void testFuzzGenericSoundModel() throws Exception {
-        int numModels = 2;
-
-        final int STATUS_UNLOADED = 0;
-        final int STATUS_LOADED = 1;
-        final int STATUS_STARTED = 2;
-
-        class ModelInfo {
-            int status;
-            GenericSoundModel model;
-
-            public ModelInfo(GenericSoundModel model, int status) {
-                this.status = status;
-                this.model = model;
-            }
-        }
-
-        Random predictableRandom = new Random(100);
-
-        ArrayList modelInfos = new ArrayList<ModelInfo>();
-        for(int i=0; i<numModels; i++) {
-            // Create sound model
-            byte[] data = new byte[1024];
-            predictableRandom.nextBytes(data);
-            UUID modelUuid = UUID.randomUUID();
-            UUID mVendorUuid = UUID.randomUUID();
-            GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
-            ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
-            modelInfos.add(modelInfo);
-        }
-
-        boolean captureTriggerAudio = true;
-        boolean allowMultipleTriggers = true;
-        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                null, null);
-        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-
-        int numOperationsToRun = 100;
-        for(int i=0; i<numOperationsToRun; i++) {
-            // Select a random model
-            int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
-            ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
-
-            // Perform a random operation
-            int operation = predictableRandom.nextInt(5);
-
-            if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
-                // Update and start sound model
-                soundTriggerService.updateSoundModel(modelInfo.model);
-                loadedModelUuids.add(modelInfo.model.getUuid());
-                modelInfo.status = STATUS_LOADED;
-            } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
-                // Start the sound model
-                int r = soundTriggerService.startRecognition(new ParcelUuid(
-                                modelInfo.model.getUuid()),
-                        spyCallback, config);
-                assertEquals("Could Not Start Recognition with code: " + r,
-                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-                modelInfo.status = STATUS_STARTED;
-            } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
-                // Send trigger to stub HAL
-                Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
-                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
-                out.writeBytes("trig " + modelInfo.model.getUuid() + "\r\n");
-                out.flush();
-                socket.close();
-
-                // Verify trigger was received
-                verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
-                reset(spyCallback);
-            } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
-                // Stop recognition
-                int r = soundTriggerService.stopRecognition(new ParcelUuid(
-                                modelInfo.model.getUuid()),
-                        spyCallback);
-                assertEquals("Could Not Stop Recognition with code: " + r,
-                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-                modelInfo.status = STATUS_LOADED;
-            } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
-                // Delete sound model
-                soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
-                loadedModelUuids.remove(modelInfo.model.getUuid());
-
-                // Confirm it was deleted
-                GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
-                        new ParcelUuid(modelInfo.model.getUuid()));
-                assertEquals(null, returnedModel);
-                modelInfo.status = STATUS_UNLOADED;
-            }
-        }
-    }
-
-    public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
-        @Override
-        public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
-        }
-
-        @Override
-        public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
-        }
-
-        @Override
-        public void onError(int status) {
-        }
-
-        @Override
-        public void onRecognitionPaused() {
-        }
-
-        @Override
-        public void onRecognitionResumed() {
-        }
-    }
-}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index ffde8c7..23efe54 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -55,6 +55,7 @@
         "cts-install-lib-host",
     ],
     data: [
+        ":StagedInstallInternalTestApp",
         ":apex.apexd_test",
         ":com.android.apex.apkrollback.test_v1",
         ":com.android.apex.apkrollback.test_v2",
diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp
index 7974462..cc8bc45 100644
--- a/tests/SystemMemoryTest/host/Android.bp
+++ b/tests/SystemMemoryTest/host/Android.bp
@@ -26,4 +26,7 @@
     srcs: ["src/**/*.java"],
     libs: ["tradefed"],
     test_suites: ["general-tests"],
+    data: [
+        ":SystemMemoryTestDevice",
+    ],
 }
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
index b313c9f..73a0a61 100644
--- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -17,6 +17,7 @@
 package android.net.vcn;
 
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import static org.junit.Assert.assertEquals;
@@ -160,6 +161,37 @@
         assertNotEquals(config, configNotEqual);
     }
 
+    private VcnConfig buildConfigRestrictTransportTest(boolean isTestMode) throws Exception {
+        VcnConfig.Builder builder =
+                new VcnConfig.Builder(mContext)
+                        .setRestrictedUnderlyingNetworkTransports(Set.of(TRANSPORT_TEST));
+        if (isTestMode) {
+            builder.setIsTestModeProfile();
+        }
+
+        for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
+            builder.addGatewayConnectionConfig(gatewayConnectionConfig);
+        }
+
+        return builder.build();
+    }
+
+    @Test
+    public void testRestrictTransportTestInTestModeProfile() throws Exception {
+        final VcnConfig config = buildConfigRestrictTransportTest(true /*  isTestMode */);
+        assertEquals(Set.of(TRANSPORT_TEST), config.getRestrictedUnderlyingNetworkTransports());
+    }
+
+    @Test
+    public void testRestrictTransportTestInNonTestModeProfile() throws Exception {
+        try {
+            buildConfigRestrictTransportTest(false /*  isTestMode */);
+            fail("Expected exception because the config is not a test mode profile");
+        } catch (Exception expected) {
+
+        }
+    }
+
     @Test
     public void testParceling() {
         final VcnConfig config = buildTestConfig(mContext);
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 629e988..2266041 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -95,6 +95,7 @@
     private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
             new NetworkCapabilities.Builder()
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                     .setSignalStrength(WIFI_RSSI)
                     .setSsid(SSID)
                     .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
@@ -509,12 +510,14 @@
             VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) {
         assertEquals(
                 expectMatch,
-                checkMatchesCellPriorityRule(
+                checkMatchesPriorityRule(
                         mVcnContext,
                         template,
                         mCellNetworkRecord,
                         SUB_GROUP,
-                        mSubscriptionSnapshot));
+                        mSubscriptionSnapshot,
+                        null /* currentlySelected */,
+                        null /* carrierConfig */));
     }
 
     @Test