Merge "Add a missing return statement."
diff --git a/Android.bp b/Android.bp
index e03f844..25238e8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -308,6 +308,8 @@
         include_dirs: [
             "frameworks/av/aidl",
             "frameworks/native/libs/permission/aidl",
+            // TODO: remove when moved to the below package
+            "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
             "packages/modules/Connectivity/framework/aidl-export",
         ],
     },
@@ -557,6 +559,9 @@
         include_dirs: [
             "frameworks/av/aidl",
             "frameworks/native/libs/permission/aidl",
+            // TODO: remove when moved to the below package
+            "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
+            "packages/modules/Connectivity/framework/aidl-export",
         ],
     },
     // These are libs from framework-internal-utils that are required (i.e. being referenced)
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index afad29c..c4795f5 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -364,6 +364,11 @@
      * @hide
      */
     public static final int REASON_SYSTEM_MODULE = 320;
+    /**
+     * Carrier privileged app.
+     * @hide
+     */
+    public static final int REASON_CARRIER_PRIVILEGED_APP = 321;
 
     /** @hide The app requests out-out. */
     public static final int REASON_OPT_OUT_REQUESTED = 1000;
@@ -440,6 +445,7 @@
             REASON_ROLE_DIALER,
             REASON_ROLE_EMERGENCY,
             REASON_SYSTEM_MODULE,
+            REASON_CARRIER_PRIVILEGED_APP,
             REASON_OPT_OUT_REQUESTED,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -749,6 +755,8 @@
                 return "ROLE_EMERGENCY";
             case REASON_SYSTEM_MODULE:
                 return "SYSTEM_MODULE";
+            case REASON_CARRIER_PRIVILEGED_APP:
+                return "CARRIER_PRIVILEGED_APP";
             case REASON_OPT_OUT_REQUESTED:
                 return "REASON_OPT_OUT_REQUESTED";
             default:
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 b2ae8ee..cea1945 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -357,6 +357,9 @@
     // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
     // (ScheduledJobStateChanged and JobStatusDumpProto).
     public static final int RESTRICTED_INDEX = 5;
+    // Putting EXEMPTED_INDEX after RESTRICTED_INDEX to make it easier for proto dumping
+    // (ScheduledJobStateChanged and JobStatusDumpProto).
+    public static final int EXEMPTED_INDEX = 6;
 
     private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
             EconomyManagerInternal.TareStateChangeListener {
@@ -2492,6 +2495,7 @@
                     shouldForceBatchJob =
                             mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
                                     && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
+                                    && job.getEffectiveStandbyBucket() != EXEMPTED_INDEX
                                     && !batchDelayExpired;
                 }
 
@@ -2764,7 +2768,7 @@
                 return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
                         ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
                         : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
-            } else if (job.getEffectivePriority() == JobInfo.PRIORITY_HIGH) {
+            } else if (job.getEffectivePriority() >= JobInfo.PRIORITY_HIGH) {
                 return mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
             } else {
                 return mConstants.RUNTIME_MIN_GUARANTEE_MS;
@@ -3086,8 +3090,10 @@
             return FREQUENT_INDEX;
         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
             return WORKING_INDEX;
-        } else {
+        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_EXEMPTED) {
             return ACTIVE_INDEX;
+        } else {
+            return EXEMPTED_INDEX;
         }
     }
 
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 0eea701..0456a9b 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
@@ -17,6 +17,7 @@
 package com.android.server.job.controllers;
 
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
@@ -844,12 +845,15 @@
      * exemptions.
      */
     public int getEffectiveStandbyBucket() {
+        final int actualBucket = getStandbyBucket();
+        if (actualBucket == EXEMPTED_INDEX) {
+            return actualBucket;
+        }
         if (uidActive || getJob().isExemptedFromAppStandby()) {
             // Treat these cases as if they're in the ACTIVE bucket so that they get throttled
             // like other ACTIVE apps.
             return ACTIVE_INDEX;
         }
-        final int actualBucket = getStandbyBucket();
         if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX
                 && mHasMediaBackupExemption) {
             // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the
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 dd5246a..c1728a3 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
@@ -21,6 +21,7 @@
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RARE_INDEX;
@@ -132,6 +133,7 @@
          */
         public long expirationTimeElapsed;
 
+        public long allowedTimePerPeriodMs;
         public long windowSizeMs;
         public int jobCountLimit;
         public int sessionCountLimit;
@@ -213,6 +215,7 @@
         @Override
         public String toString() {
             return "expirationTime=" + expirationTimeElapsed + ", "
+                    + "allowedTimePerPeriodMs=" + allowedTimePerPeriodMs + ", "
                     + "windowSizeMs=" + windowSizeMs + ", "
                     + "jobCountLimit=" + jobCountLimit + ", "
                     + "sessionCountLimit=" + sessionCountLimit + ", "
@@ -236,6 +239,7 @@
             if (obj instanceof ExecutionStats) {
                 ExecutionStats other = (ExecutionStats) obj;
                 return this.expirationTimeElapsed == other.expirationTimeElapsed
+                        && this.allowedTimePerPeriodMs == other.allowedTimePerPeriodMs
                         && this.windowSizeMs == other.windowSizeMs
                         && this.jobCountLimit == other.jobCountLimit
                         && this.sessionCountLimit == other.sessionCountLimit
@@ -261,6 +265,7 @@
         public int hashCode() {
             int result = 0;
             result = 31 * result + hashLong(expirationTimeElapsed);
+            result = 31 * result + hashLong(allowedTimePerPeriodMs);
             result = 31 * result + hashLong(windowSizeMs);
             result = 31 * result + hashLong(jobCountLimit);
             result = 31 * result + hashLong(sessionCountLimit);
@@ -350,7 +355,15 @@
     private boolean mIsEnabled;
 
     /** How much time each app will have to run jobs within their standby bucket window. */
-    private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
+    private final long[] mAllowedTimePerPeriodMs = new long[]{
+            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
+            0, // NEVER
+            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+    };
 
     /**
      * The maximum amount of time an app can have its jobs running within a {@link #MAX_PERIOD_MS}
@@ -365,12 +378,6 @@
     private long mQuotaBufferMs = QcConstants.DEFAULT_IN_QUOTA_BUFFER_MS;
 
     /**
-     * {@link #mAllowedTimePerPeriodMs} - {@link #mQuotaBufferMs}. This can be used to determine
-     * when an app will have enough quota to transition from out-of-quota to in-quota.
-     */
-    private long mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
-
-    /**
      * {@link #mMaxExecutionTimeMs} - {@link #mQuotaBufferMs}. This can be used to determine when an
      * app will have enough quota to transition from out-of-quota to in-quota.
      */
@@ -450,7 +457,8 @@
             QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
             QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS,
             0, // NEVER
-            QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS
+            QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS,
+            QcConstants.DEFAULT_WINDOW_SIZE_EXEMPTED_MS
     };
 
     /** The maximum period any bucket can have. */
@@ -469,7 +477,8 @@
             QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT,
             QcConstants.DEFAULT_MAX_JOB_COUNT_RARE,
             0, // NEVER
-            QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED
+            QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED,
+            QcConstants.DEFAULT_MAX_JOB_COUNT_EXEMPTED
     };
 
     /**
@@ -487,6 +496,7 @@
             QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE,
             0, // NEVER
             QcConstants.DEFAULT_MAX_SESSION_COUNT_RESTRICTED,
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_EXEMPTED,
     };
 
     /**
@@ -506,7 +516,8 @@
             QcConstants.DEFAULT_EJ_LIMIT_FREQUENT_MS,
             QcConstants.DEFAULT_EJ_LIMIT_RARE_MS,
             0, // NEVER
-            QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS
+            QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS,
+            QcConstants.DEFAULT_EJ_LIMIT_EXEMPTED_MS
     };
 
     private long mEjLimitAdditionInstallerMs = QcConstants.DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS;
@@ -823,6 +834,11 @@
         if (mService.isBatteryCharging()) {
             return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
         }
+        if (jobStatus.getEffectiveStandbyBucket() == EXEMPTED_INDEX) {
+            return Math.max(mEJLimitsMs[EXEMPTED_INDEX] / 2,
+                    getTimeUntilEJQuotaConsumedLocked(
+                            jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()));
+        }
         if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) {
             return Math.max(mEJLimitsMs[ACTIVE_INDEX] / 2,
                     getTimeUntilEJQuotaConsumedLocked(
@@ -929,9 +945,11 @@
 
         final long minSurplus;
         if (priority <= JobInfo.PRIORITY_MIN) {
-            minSurplus = (long) (mAllowedTimePerPeriodMs * mAllowedTimeSurplusPriorityMin);
+            minSurplus = (long)
+                    (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityMin);
         } else if (priority <= JobInfo.PRIORITY_LOW) {
-            minSurplus = (long) (mAllowedTimePerPeriodMs * mAllowedTimeSurplusPriorityLow);
+            minSurplus = (long)
+                    (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityLow);
         } else {
             minSurplus = 0;
         }
@@ -989,7 +1007,7 @@
     }
 
     private long getRemainingExecutionTimeLocked(@NonNull ExecutionStats stats) {
-        return Math.min(mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs,
+        return Math.min(stats.allowedTimePerPeriodMs - stats.executionTimeInWindowMs,
                 mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
     }
 
@@ -1068,15 +1086,15 @@
         if (sessions == null || sessions.size() == 0) {
             // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
             // essentially run until they reach the maximum limit.
-            if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
+            if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
                 return mMaxExecutionTimeMs;
             }
-            return mAllowedTimePerPeriodMs;
+            return mAllowedTimePerPeriodMs[standbyBucket];
         }
 
         final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
         final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
-        final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(jobPriority);
+        final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(standbyBucket, jobPriority);
         final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
         final long maxExecutionTimeRemainingMs =
                 mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
@@ -1087,7 +1105,7 @@
 
         // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
         // essentially run until they reach the maximum limit.
-        if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
+        if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
             return calculateTimeUntilQuotaConsumedLocked(
                     sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
         }
@@ -1103,14 +1121,19 @@
                         sessions, startWindowElapsed, allowedTimeRemainingMs));
     }
 
-    private long getAllowedTimePerPeriodMs(@JobInfo.Priority int jobPriority) {
+    private long getAllowedTimePerPeriodMs(int standbyBucket, @JobInfo.Priority int jobPriority) {
+        return getAllowedTimePerPeriodMs(mAllowedTimePerPeriodMs[standbyBucket], jobPriority);
+    }
+
+    private long getAllowedTimePerPeriodMs(long initialAllowedTime,
+            @JobInfo.Priority int jobPriority) {
         if (jobPriority <= JobInfo.PRIORITY_MIN) {
-            return (long) (mAllowedTimePerPeriodMs * (1 - mAllowedTimeSurplusPriorityMin));
+            return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityMin));
         }
         if (jobPriority <= JobInfo.PRIORITY_LOW) {
-            return (long) (mAllowedTimePerPeriodMs * (1 - mAllowedTimeSurplusPriorityLow));
+            return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityLow));
         }
-        return mAllowedTimePerPeriodMs;
+        return initialAllowedTime;
     }
 
     /**
@@ -1237,16 +1260,19 @@
             appStats[standbyBucket] = stats;
         }
         if (refreshStatsIfOld) {
+            final long bucketAllowedTimeMs = mAllowedTimePerPeriodMs[standbyBucket];
             final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
             final int jobCountLimit = mMaxBucketJobCounts[standbyBucket];
             final int sessionCountLimit = mMaxBucketSessionCounts[standbyBucket];
             Timer timer = mPkgTimers.get(userId, packageName);
             if ((timer != null && timer.isActive())
                     || stats.expirationTimeElapsed <= sElapsedRealtimeClock.millis()
+                    || stats.allowedTimePerPeriodMs != bucketAllowedTimeMs
                     || stats.windowSizeMs != bucketWindowSizeMs
                     || stats.jobCountLimit != jobCountLimit
                     || stats.sessionCountLimit != sessionCountLimit) {
                 // The stats are no longer valid.
+                stats.allowedTimePerPeriodMs = bucketAllowedTimeMs;
                 stats.windowSizeMs = bucketWindowSizeMs;
                 stats.jobCountLimit = jobCountLimit;
                 stats.sessionCountLimit = sessionCountLimit;
@@ -1272,8 +1298,11 @@
         } else {
             stats.inQuotaTimeElapsed = 0;
         }
-        final long allowedTimeMinMs = getAllowedTimePerPeriodMs(JobInfo.PRIORITY_MIN);
-        final long allowedTimeLowMs = getAllowedTimePerPeriodMs(JobInfo.PRIORITY_LOW);
+        final long allowedTimeMinMs =
+                getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_MIN);
+        final long allowedTimeLowMs =
+                getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_LOW);
+        final long allowedTimeIntoQuotaMs = stats.allowedTimePerPeriodMs - mQuotaBufferMs;
 
         Timer timer = mPkgTimers.get(userId, packageName);
         final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -1287,9 +1316,9 @@
             // If the timer is active, the value will be stale at the next method call, so
             // invalidate now.
             stats.expirationTimeElapsed = nowElapsed;
-            if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+            if (stats.executionTimeInWindowMs >= allowedTimeIntoQuotaMs) {
                 stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
-                        nowElapsed - mAllowedTimeIntoQuotaMs + stats.windowSizeMs);
+                        nowElapsed - allowedTimeIntoQuotaMs + stats.windowSizeMs);
             }
             if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
                 stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
@@ -1346,9 +1375,9 @@
 
                 stats.executionTimeInWindowMs += session.endTimeElapsed - start;
                 stats.bgJobCountInWindow += session.bgJobCount;
-                if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+                if (stats.executionTimeInWindowMs >= allowedTimeIntoQuotaMs) {
                     stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
-                            start + stats.executionTimeInWindowMs - mAllowedTimeIntoQuotaMs
+                            start + stats.executionTimeInWindowMs - allowedTimeIntoQuotaMs
                                     + stats.windowSizeMs);
                 }
                 if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
@@ -1697,7 +1726,7 @@
                 if (js.setQuotaConstraintSatisfied(nowElapsed, true)) {
                     changedJobs.add(js);
                 }
-            } else if (realStandbyBucket != ACTIVE_INDEX
+            } else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX
                     && realStandbyBucket == js.getEffectiveStandbyBucket()
                     && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
                 // An app in the ACTIVE bucket may be out of quota while the job could be in quota
@@ -1842,7 +1871,8 @@
             }
         }
         final boolean inRegularQuota =
-                stats.executionTimeInWindowMs < getAllowedTimePerPeriodMs(minPriority)
+                stats.executionTimeInWindowMs
+                        < getAllowedTimePerPeriodMs(standbyBucket, minPriority)
                         && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
                         && isUnderJobCountQuota
                         && isUnderTimingSessionCountQuota;
@@ -2921,9 +2951,29 @@
         /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
         private static final String QC_CONSTANT_PREFIX = "qc_";
 
+        /**
+         * Previously used keys:
+         *   * allowed_time_per_period_ms -- No longer used after splitting by bucket
+         */
+
         @VisibleForTesting
-        static final String KEY_ALLOWED_TIME_PER_PERIOD_MS =
-                QC_CONSTANT_PREFIX + "allowed_time_per_period_ms";
+        static final String KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+                QC_CONSTANT_PREFIX + "allowed_time_per_period_exempted_ms";
+        @VisibleForTesting
+        static final String KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                QC_CONSTANT_PREFIX + "allowed_time_per_period_active_ms";
+        @VisibleForTesting
+        static final String KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
+                QC_CONSTANT_PREFIX + "allowed_time_per_period_working_ms";
+        @VisibleForTesting
+        static final String KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+                QC_CONSTANT_PREFIX + "allowed_time_per_period_frequent_ms";
+        @VisibleForTesting
+        static final String KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS =
+                QC_CONSTANT_PREFIX + "allowed_time_per_period_rare_ms";
+        @VisibleForTesting
+        static final String KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
+                QC_CONSTANT_PREFIX + "allowed_time_per_period_restricted_ms";
         @VisibleForTesting
         static final String KEY_IN_QUOTA_BUFFER_MS =
                 QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@@ -2934,6 +2984,9 @@
         static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
                 QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_min";
         @VisibleForTesting
+        static final String KEY_WINDOW_SIZE_EXEMPTED_MS =
+                QC_CONSTANT_PREFIX + "window_size_exempted_ms";
+        @VisibleForTesting
         static final String KEY_WINDOW_SIZE_ACTIVE_MS =
                 QC_CONSTANT_PREFIX + "window_size_active_ms";
         @VisibleForTesting
@@ -2952,6 +3005,9 @@
         static final String KEY_MAX_EXECUTION_TIME_MS =
                 QC_CONSTANT_PREFIX + "max_execution_time_ms";
         @VisibleForTesting
+        static final String KEY_MAX_JOB_COUNT_EXEMPTED =
+                QC_CONSTANT_PREFIX + "max_job_count_exempted";
+        @VisibleForTesting
         static final String KEY_MAX_JOB_COUNT_ACTIVE =
                 QC_CONSTANT_PREFIX + "max_job_count_active";
         @VisibleForTesting
@@ -2973,6 +3029,9 @@
         static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
                 QC_CONSTANT_PREFIX + "max_job_count_per_rate_limiting_window";
         @VisibleForTesting
+        static final String KEY_MAX_SESSION_COUNT_EXEMPTED =
+                QC_CONSTANT_PREFIX + "max_session_count_exempted";
+        @VisibleForTesting
         static final String KEY_MAX_SESSION_COUNT_ACTIVE =
                 QC_CONSTANT_PREFIX + "max_session_count_active";
         @VisibleForTesting
@@ -2997,6 +3056,9 @@
         static final String KEY_MIN_QUOTA_CHECK_DELAY_MS =
                 QC_CONSTANT_PREFIX + "min_quota_check_delay_ms";
         @VisibleForTesting
+        static final String KEY_EJ_LIMIT_EXEMPTED_MS =
+                QC_CONSTANT_PREFIX + "ej_limit_exempted_ms";
+        @VisibleForTesting
         static final String KEY_EJ_LIMIT_ACTIVE_MS =
                 QC_CONSTANT_PREFIX + "ej_limit_active_ms";
         @VisibleForTesting
@@ -3039,14 +3101,26 @@
         static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
                 QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
 
-        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
                 30 * 1000L; // 30 seconds
         private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW = .25f;
         private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN = .5f;
+        private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
+                DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
         private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; // ACTIVE apps can run jobs at any time
+                DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
         private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
                 2 * 60 * 60 * 1000L; // 2 hours
         private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
@@ -3060,8 +3134,9 @@
         private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
                 MINUTE_IN_MILLIS;
         private static final int DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 20;
-        private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE =
+        private static final int DEFAULT_MAX_JOB_COUNT_EXEMPTED =
                 75; // 75/window = 450/hr = 1/session
+        private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_EXEMPTED;
         private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
                 (int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
         private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
@@ -3069,8 +3144,10 @@
         private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
                 (int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
         private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10;
-        private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
+        private static final int DEFAULT_MAX_SESSION_COUNT_EXEMPTED =
                 75; // 450/hr
+        private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
+                DEFAULT_MAX_SESSION_COUNT_EXEMPTED;
         private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
                 10; // 5/hr
         private static final int DEFAULT_MAX_SESSION_COUNT_FREQUENT =
@@ -3081,6 +3158,7 @@
         private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
         private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
         private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS;
+        private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 45 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
         private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
@@ -3096,8 +3174,39 @@
         private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS;
 
-        /** How much time each app will have to run jobs within their standby bucket window. */
-        public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
+        /**
+         * How much time each app in the exempted bucket will have to run jobs within their standby
+         * bucket window.
+         */
+        public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+                DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+        /**
+         * How much time each app in the active bucket will have to run jobs within their standby
+         * bucket window.
+         */
+        public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        /**
+         * How much time each app in the working set bucket will have to run jobs within their
+         * standby bucket window.
+         */
+        public long ALLOWED_TIME_PER_PERIOD_WORKING_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS;
+        /**
+         * How much time each app in the frequent bucket will have to run jobs within their standby
+         * bucket window.
+         */
+        public long ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+                DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS;
+        /**
+         * How much time each app in the rare bucket will have to run jobs within their standby
+         * bucket window.
+         */
+        public long ALLOWED_TIME_PER_PERIOD_RARE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS;
+        /**
+         * How much time each app in the restricted bucket will have to run jobs within their
+         * standby bucket window.
+         */
+        public long ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
+                DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS;
 
         /**
          * How much time the package should have before transitioning from out-of-quota to in-quota.
@@ -3106,7 +3215,7 @@
         public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
 
         /**
-         * The percentage of {@link #ALLOWED_TIME_PER_PERIOD_MS} that should not be used by
+         * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
          * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
          * surplus of this amount of remaining allowed time before we start running low priority
          * jobs.
@@ -3114,7 +3223,7 @@
         public float ALLOWED_TIME_SURPLUS_PRIORITY_LOW = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
 
         /**
-         * The percentage of {@link #ALLOWED_TIME_PER_PERIOD_MS} that should not be used by
+         * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
          * {@link JobInfo#PRIORITY_MIN low priority} jobs. In other words, there must be a minimum
          * surplus of this amount of remaining allowed time before we start running min priority
          * jobs.
@@ -3123,35 +3232,42 @@
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_WINDOW_SIZE_EXEMPTED_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_ACTIVE_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_WORKING_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_FREQUENT_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_RARE_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS} within the past
          * WINDOW_SIZE_MS.
          */
         public long WINDOW_SIZE_RESTRICTED_MS = DEFAULT_WINDOW_SIZE_RESTRICTED_MS;
@@ -3165,6 +3281,12 @@
          * The maximum number of jobs an app can run within this particular standby bucket's
          * window size.
          */
+        public int MAX_JOB_COUNT_EXEMPTED = DEFAULT_MAX_JOB_COUNT_EXEMPTED;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
         public int MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_ACTIVE;
 
         /**
@@ -3204,6 +3326,12 @@
          * The maximum number of {@link TimingSession TimingSessions} an app can run within this
          * particular standby bucket's window size.
          */
+        public int MAX_SESSION_COUNT_EXEMPTED = DEFAULT_MAX_SESSION_COUNT_EXEMPTED;
+
+        /**
+         * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+         * particular standby bucket's window size.
+         */
         public int MAX_SESSION_COUNT_ACTIVE = DEFAULT_MAX_SESSION_COUNT_ACTIVE;
 
         /**
@@ -3232,7 +3360,7 @@
 
         /**
          * The maximum number of {@link TimingSession TimingSessions} that can run within the past
-         * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
+         * {@link #RATE_LIMITING_WINDOW_MS}.
          */
         public int MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
                 DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
@@ -3275,6 +3403,13 @@
          * standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring
          * in any rewards or free EJs).
          */
+        public long EJ_LIMIT_EXEMPTED_MS = DEFAULT_EJ_LIMIT_EXEMPTED_MS;
+
+        /**
+         * The total expedited job session limit of the particular standby bucket. Apps in this
+         * standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring
+         * in any rewards or free EJs).
+         */
         public long EJ_LIMIT_ACTIVE_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
 
         /**
@@ -3358,7 +3493,12 @@
         public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
                 @NonNull String key) {
             switch (key) {
-                case KEY_ALLOWED_TIME_PER_PERIOD_MS:
+                case KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS:
+                case KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS:
+                case KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS:
+                case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
+                case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
+                case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
                 case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW:
                 case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN:
                 case KEY_IN_QUOTA_BUFFER_MS:
@@ -3388,6 +3528,15 @@
                     updateEJLimitConstantsLocked();
                     break;
 
+                case KEY_MAX_JOB_COUNT_EXEMPTED:
+                    MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED);
+                    int newExemptedMaxJobCount =
+                            Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_EXEMPTED);
+                    if (mMaxBucketJobCounts[EXEMPTED_INDEX] != newExemptedMaxJobCount) {
+                        mMaxBucketJobCounts[EXEMPTED_INDEX] = newExemptedMaxJobCount;
+                        mShouldReevaluateConstraints = true;
+                    }
+                    break;
                 case KEY_MAX_JOB_COUNT_ACTIVE:
                     MAX_JOB_COUNT_ACTIVE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_ACTIVE);
                     int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
@@ -3432,6 +3581,16 @@
                         mShouldReevaluateConstraints = true;
                     }
                     break;
+                case KEY_MAX_SESSION_COUNT_EXEMPTED:
+                    MAX_SESSION_COUNT_EXEMPTED =
+                            properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_EXEMPTED);
+                    int newExemptedMaxSessionCount =
+                            Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_EXEMPTED);
+                    if (mMaxBucketSessionCounts[EXEMPTED_INDEX] != newExemptedMaxSessionCount) {
+                        mMaxBucketSessionCounts[EXEMPTED_INDEX] = newExemptedMaxSessionCount;
+                        mShouldReevaluateConstraints = true;
+                    }
+                    break;
                 case KEY_MAX_SESSION_COUNT_ACTIVE:
                     MAX_SESSION_COUNT_ACTIVE =
                             properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
@@ -3579,15 +3738,34 @@
             // Query the values as an atomic set.
             final DeviceConfig.Properties properties = DeviceConfig.getProperties(
                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
-                    KEY_ALLOWED_TIME_PER_PERIOD_MS, KEY_IN_QUOTA_BUFFER_MS,
+                    KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+                    KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+                    KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                    KEY_IN_QUOTA_BUFFER_MS,
                     KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
-                    KEY_MAX_EXECUTION_TIME_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
+                    KEY_MAX_EXECUTION_TIME_MS,
+                    KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
                     KEY_WINDOW_SIZE_WORKING_MS,
                     KEY_WINDOW_SIZE_FREQUENT_MS, KEY_WINDOW_SIZE_RARE_MS,
                     KEY_WINDOW_SIZE_RESTRICTED_MS);
-            ALLOWED_TIME_PER_PERIOD_MS =
-                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
+            ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                            DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+            ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+                            DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+            ALLOWED_TIME_PER_PERIOD_WORKING_MS =
+                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                            DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
+            ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+                            DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS);
+            ALLOWED_TIME_PER_PERIOD_RARE_MS =
+                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS,
+                            DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS);
+            ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
+                    properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                            DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
             ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
                     properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW,
                             DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW);
@@ -3598,6 +3776,8 @@
                     DEFAULT_IN_QUOTA_BUFFER_MS);
             MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
                     DEFAULT_MAX_EXECUTION_TIME_MS);
+            WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
+                    DEFAULT_WINDOW_SIZE_EXEMPTED_MS);
             WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
                     DEFAULT_WINDOW_SIZE_ACTIVE_MS);
             WINDOW_SIZE_WORKING_MS =
@@ -3618,20 +3798,55 @@
                 mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
                 mShouldReevaluateConstraints = true;
             }
-            long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
-                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
-            if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
-                mAllowedTimePerPeriodMs = newAllowedTimeMs;
-                mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+            long minAllowedTimeMs = Long.MAX_VALUE;
+            long newAllowedTimeExemptedMs = Math.min(mMaxExecutionTimeMs,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+            minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeExemptedMs);
+            if (mAllowedTimePerPeriodMs[EXEMPTED_INDEX] != newAllowedTimeExemptedMs) {
+                mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = newAllowedTimeExemptedMs;
+                mShouldReevaluateConstraints = true;
+            }
+            long newAllowedTimeActiveMs = Math.min(mMaxExecutionTimeMs,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+            minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeActiveMs);
+            if (mAllowedTimePerPeriodMs[ACTIVE_INDEX] != newAllowedTimeActiveMs) {
+                mAllowedTimePerPeriodMs[ACTIVE_INDEX] = newAllowedTimeActiveMs;
+                mShouldReevaluateConstraints = true;
+            }
+            long newAllowedTimeWorkingMs = Math.min(mMaxExecutionTimeMs,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_WORKING_MS));
+            minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeWorkingMs);
+            if (mAllowedTimePerPeriodMs[WORKING_INDEX] != newAllowedTimeWorkingMs) {
+                mAllowedTimePerPeriodMs[WORKING_INDEX] = newAllowedTimeWorkingMs;
+                mShouldReevaluateConstraints = true;
+            }
+            long newAllowedTimeFrequentMs = Math.min(mMaxExecutionTimeMs,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_FREQUENT_MS));
+            minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeFrequentMs);
+            if (mAllowedTimePerPeriodMs[FREQUENT_INDEX] != newAllowedTimeFrequentMs) {
+                mAllowedTimePerPeriodMs[FREQUENT_INDEX] = newAllowedTimeFrequentMs;
+                mShouldReevaluateConstraints = true;
+            }
+            long newAllowedTimeRareMs = Math.min(mMaxExecutionTimeMs,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_RARE_MS));
+            minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeRareMs);
+            if (mAllowedTimePerPeriodMs[RARE_INDEX] != newAllowedTimeRareMs) {
+                mAllowedTimePerPeriodMs[RARE_INDEX] = newAllowedTimeRareMs;
+                mShouldReevaluateConstraints = true;
+            }
+            long newAllowedTimeRestrictedMs = Math.min(mMaxExecutionTimeMs,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS));
+            minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeRestrictedMs);
+            if (mAllowedTimePerPeriodMs[RESTRICTED_INDEX] != newAllowedTimeRestrictedMs) {
+                mAllowedTimePerPeriodMs[RESTRICTED_INDEX] = newAllowedTimeRestrictedMs;
                 mShouldReevaluateConstraints = true;
             }
             // Make sure quota buffer is non-negative, not greater than allowed time per period,
             // and no more than 5 minutes.
-            long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
+            long newQuotaBufferMs = Math.max(0, Math.min(minAllowedTimeMs,
                     Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
             if (mQuotaBufferMs != newQuotaBufferMs) {
                 mQuotaBufferMs = newQuotaBufferMs;
-                mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
                 mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
                 mShouldReevaluateConstraints = true;
             }
@@ -3652,32 +3867,38 @@
                 mAllowedTimeSurplusPriorityMin = newAllowedTimeSurplusPriorityMin;
                 mShouldReevaluateConstraints = true;
             }
-            long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+            long newExemptedPeriodMs = Math.max(mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+                    Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
+            if (mBucketPeriodsMs[EXEMPTED_INDEX] != newExemptedPeriodMs) {
+                mBucketPeriodsMs[EXEMPTED_INDEX] = newExemptedPeriodMs;
+                mShouldReevaluateConstraints = true;
+            }
+            long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs[ACTIVE_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
             if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
                 mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
                 mShouldReevaluateConstraints = true;
             }
-            long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+            long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs[WORKING_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
             if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
                 mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
                 mShouldReevaluateConstraints = true;
             }
-            long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+            long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs[FREQUENT_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
             if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
                 mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
                 mShouldReevaluateConstraints = true;
             }
-            long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+            long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs[RARE_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
             if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
                 mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
                 mShouldReevaluateConstraints = true;
             }
             // Fit in the range [allowed time (10 mins), 1 week].
-            long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+            long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs[RESTRICTED_INDEX],
                     Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
             if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
                 mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
@@ -3740,11 +3961,14 @@
             // Query the values as an atomic set.
             final DeviceConfig.Properties properties = DeviceConfig.getProperties(
                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_EJ_LIMIT_EXEMPTED_MS,
                     KEY_EJ_LIMIT_ACTIVE_MS, KEY_EJ_LIMIT_WORKING_MS,
                     KEY_EJ_LIMIT_FREQUENT_MS, KEY_EJ_LIMIT_RARE_MS,
                     KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_LIMIT_ADDITION_SPECIAL_MS,
                     KEY_EJ_LIMIT_ADDITION_INSTALLER_MS,
                     KEY_EJ_WINDOW_SIZE_MS);
+            EJ_LIMIT_EXEMPTED_MS = properties.getLong(
+                    KEY_EJ_LIMIT_EXEMPTED_MS, DEFAULT_EJ_LIMIT_EXEMPTED_MS);
             EJ_LIMIT_ACTIVE_MS = properties.getLong(
                     KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
             EJ_LIMIT_WORKING_MS = properties.getLong(
@@ -3770,8 +3994,15 @@
                 mShouldReevaluateConstraints = true;
             }
             // The limit must be in the range [15 minutes, window size].
+            long newExemptLimitMs = Math.max(15 * MINUTE_IN_MILLIS,
+                    Math.min(newWindowSizeMs, EJ_LIMIT_EXEMPTED_MS));
+            if (mEJLimitsMs[EXEMPTED_INDEX] != newExemptLimitMs) {
+                mEJLimitsMs[EXEMPTED_INDEX] = newExemptLimitMs;
+                mShouldReevaluateConstraints = true;
+            }
+            // The limit must be in the range [15 minutes, exempted limit].
             long newActiveLimitMs = Math.max(15 * MINUTE_IN_MILLIS,
-                    Math.min(newWindowSizeMs, EJ_LIMIT_ACTIVE_MS));
+                    Math.min(newExemptLimitMs, EJ_LIMIT_ACTIVE_MS));
             if (mEJLimitsMs[ACTIVE_INDEX] != newActiveLimitMs) {
                 mEJLimitsMs[ACTIVE_INDEX] = newActiveLimitMs;
                 mShouldReevaluateConstraints = true;
@@ -3823,18 +4054,31 @@
             pw.println();
             pw.println("QuotaController:");
             pw.increaseIndent();
-            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_MS, ALLOWED_TIME_PER_PERIOD_MS).println();
+            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS)
+                    .println();
+            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS)
+                    .println();
+            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, ALLOWED_TIME_PER_PERIOD_WORKING_MS)
+                    .println();
+            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, ALLOWED_TIME_PER_PERIOD_FREQUENT_MS)
+                    .println();
+            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, ALLOWED_TIME_PER_PERIOD_RARE_MS)
+                    .println();
+            pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                    ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
             pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, ALLOWED_TIME_SURPLUS_PRIORITY_LOW)
                     .println();
             pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, ALLOWED_TIME_SURPLUS_PRIORITY_MIN)
                     .println();
             pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
+            pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
             pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
             pw.print(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println();
             pw.print(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println();
             pw.print(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println();
             pw.print(KEY_WINDOW_SIZE_RESTRICTED_MS, WINDOW_SIZE_RESTRICTED_MS).println();
             pw.print(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println();
+            pw.print(KEY_MAX_JOB_COUNT_EXEMPTED, MAX_JOB_COUNT_EXEMPTED).println();
             pw.print(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println();
             pw.print(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
             pw.print(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
@@ -3843,6 +4087,7 @@
             pw.print(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println();
             pw.print(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
                     MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println();
+            pw.print(KEY_MAX_SESSION_COUNT_EXEMPTED, MAX_SESSION_COUNT_EXEMPTED).println();
             pw.print(KEY_MAX_SESSION_COUNT_ACTIVE, MAX_SESSION_COUNT_ACTIVE).println();
             pw.print(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
             pw.print(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
@@ -3854,6 +4099,7 @@
                     TIMING_SESSION_COALESCING_DURATION_MS).println();
             pw.print(KEY_MIN_QUOTA_CHECK_DELAY_MS, MIN_QUOTA_CHECK_DELAY_MS).println();
 
+            pw.print(KEY_EJ_LIMIT_EXEMPTED_MS, EJ_LIMIT_EXEMPTED_MS).println();
             pw.print(KEY_EJ_LIMIT_ACTIVE_MS, EJ_LIMIT_ACTIVE_MS).println();
             pw.print(KEY_EJ_LIMIT_WORKING_MS, EJ_LIMIT_WORKING_MS).println();
             pw.print(KEY_EJ_LIMIT_FREQUENT_MS, EJ_LIMIT_FREQUENT_MS).println();
@@ -3875,8 +4121,6 @@
 
         private void dump(ProtoOutputStream proto) {
             final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
-            proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
-                    ALLOWED_TIME_PER_PERIOD_MS);
             proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS);
             proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
                     WINDOW_SIZE_ACTIVE_MS);
@@ -3946,7 +4190,7 @@
     //////////////////////// TESTING HELPERS /////////////////////////////
 
     @VisibleForTesting
-    long getAllowedTimePerPeriodMs() {
+    long[] getAllowedTimePerPeriodMs() {
         return mAllowedTimePerPeriodMs;
     }
 
diff --git a/core/api/current.txt b/core/api/current.txt
index c29f3ae..40f589d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -326,6 +326,9 @@
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowClickWhenDisabled = 16844312; // 0x1010618
     field public static final int allowEmbedded = 16843765; // 0x10103f5
+    field public static final int allowGameAngleDriver;
+    field public static final int allowGameDownscaling;
+    field public static final int allowGameFpsOverride;
     field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
@@ -1435,10 +1438,12 @@
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportedTypes;
     field public static final int supportsAssist = 16844016; // 0x10104f0
+    field public static final int supportsBatteryGameMode;
     field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
+    field public static final int supportsPerformanceGameMode;
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
     field public static final int supportsRtl = 16843695; // 0x10103af
     field public static final int supportsStylusHandwriting;
@@ -3130,6 +3135,11 @@
     field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
     field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
     field public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; // 0xf
+    field public static final int GLOBAL_ACTION_DPAD_CENTER = 20; // 0x14
+    field public static final int GLOBAL_ACTION_DPAD_DOWN = 17; // 0x11
+    field public static final int GLOBAL_ACTION_DPAD_LEFT = 18; // 0x12
+    field public static final int GLOBAL_ACTION_DPAD_RIGHT = 19; // 0x13
+    field public static final int GLOBAL_ACTION_DPAD_UP = 16; // 0x10
     field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
     field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
     field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
@@ -31425,6 +31435,9 @@
     method @Nullable public byte[] createByteArray();
     method @Nullable public char[] createCharArray();
     method @Nullable public double[] createDoubleArray();
+    method @Nullable public <T> T createFixedArray(@NonNull Class<T>, @NonNull int...);
+    method @Nullable public <T, S extends android.os.IInterface> T createFixedArray(@NonNull Class<T>, @NonNull java.util.function.Function<android.os.IBinder,S>, @NonNull int...);
+    method @Nullable public <T, S extends android.os.Parcelable> T createFixedArray(@NonNull Class<T>, @NonNull android.os.Parcelable.Creator<S>, @NonNull int...);
     method @Nullable public float[] createFloatArray();
     method @Nullable public int[] createIntArray();
     method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>);
@@ -31466,6 +31479,9 @@
     method public void readException();
     method public void readException(int, String);
     method public android.os.ParcelFileDescriptor readFileDescriptor();
+    method public <T> void readFixedArray(@NonNull T);
+    method public <T, S extends android.os.IInterface> void readFixedArray(@NonNull T, @NonNull java.util.function.Function<android.os.IBinder,S>);
+    method public <T, S extends android.os.Parcelable> void readFixedArray(@NonNull T, @NonNull android.os.Parcelable.Creator<S>);
     method public float readFloat();
     method public void readFloatArray(@NonNull float[]);
     method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
@@ -31509,6 +31525,7 @@
     method public void setDataCapacity(int);
     method public void setDataPosition(int);
     method public void setDataSize(int);
+    method public void setPropagateAllowBlocking();
     method public void unmarshall(@NonNull byte[], int, int);
     method public void writeArray(@Nullable Object[]);
     method public void writeBinderArray(@Nullable android.os.IBinder[]);
@@ -31526,6 +31543,7 @@
     method public void writeDoubleArray(@Nullable double[]);
     method public void writeException(@NonNull Exception);
     method public void writeFileDescriptor(@NonNull java.io.FileDescriptor);
+    method public <T> void writeFixedArray(@Nullable T, int, @NonNull int...);
     method public void writeFloat(float);
     method public void writeFloatArray(@Nullable float[]);
     method public void writeInt(int);
@@ -41221,6 +41239,7 @@
     field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
     field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
     field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
+    field public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "is_opportunistic_subscription_bool";
     field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
     field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
     field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array";
@@ -41305,6 +41324,7 @@
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+    field public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string";
     field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
     field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
     field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
@@ -41348,6 +41368,7 @@
     field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
     field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
     field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+    field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
     field public static final int SERVICE_CLASS_NONE = 0; // 0x0
     field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
     field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 6cde547..6e7bc76 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -78,9 +78,9 @@
 package android.app.admin {
 
   public class DevicePolicyManager {
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void acknowledgeNewUserDisclaimer();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getLogoutUser();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int logoutUser();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int logoutUser();
     field public static final String ACTION_SHOW_NEW_USER_DISCLAIMER = "android.app.action.SHOW_NEW_USER_DISCLAIMER";
   }
 
@@ -141,6 +141,7 @@
 
   public abstract class PackageManager {
     method @NonNull public String getPermissionControllerPackageName();
+    method @NonNull public String getSupplementalProcessPackageName();
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 49a1d62..1cba58c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -76,6 +76,7 @@
     field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
     field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
     field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
+    field public static final String BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE = "android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE";
     field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
     field public static final String BRICK = "android.permission.BRICK";
     field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
@@ -191,6 +192,7 @@
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
+    field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION";
     field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
     field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
     field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
@@ -908,6 +910,19 @@
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int);
+    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info);
+    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferSenderDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+    field public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; // 0x0
+    field public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; // 0x1
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1; // 0x1
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0; // 0x0
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8; // 0x8
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6; // 0x6
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4; // 0x4
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2; // 0x2
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; // 0x7
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; // 0x5
+    field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; // 0x3
     field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1
     field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0
   }
@@ -2554,6 +2569,107 @@
 
 }
 
+package android.app.wallpapereffectsgeneration {
+
+  public final class CameraAttributes implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public float[] getAnchorPointInOutputUvSpace();
+    method @NonNull public float[] getAnchorPointInWorldSpace();
+    method public float getCameraOrbitPitchDegrees();
+    method public float getCameraOrbitYawDegrees();
+    method public float getDollyDistanceInWorldSpace();
+    method public float getFrustumFarInWorldSpace();
+    method public float getFrustumNearInWorldSpace();
+    method public float getVerticalFovDegrees();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CameraAttributes> CREATOR;
+  }
+
+  public static final class CameraAttributes.Builder {
+    ctor public CameraAttributes.Builder(@NonNull float[], @NonNull float[]);
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes build();
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitPitchDegrees(@FloatRange(from=-90.0F, to=90.0f) float);
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitYawDegrees(@FloatRange(from=-180.0F, to=180.0f) float);
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setDollyDistanceInWorldSpace(float);
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumFarInWorldSpace(@FloatRange(from=0.0f) float);
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumNearInWorldSpace(@FloatRange(from=0.0f) float);
+    method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setVerticalFovDegrees(@FloatRange(from=0.0f, to=180.0f, fromInclusive=false) float);
+  }
+
+  public final class CinematicEffectRequest implements android.os.Parcelable {
+    ctor public CinematicEffectRequest(@NonNull String, @NonNull android.graphics.Bitmap);
+    method public int describeContents();
+    method @NonNull public android.graphics.Bitmap getBitmap();
+    method @NonNull public String getTaskId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectRequest> CREATOR;
+  }
+
+  public final class CinematicEffectResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getEndKeyFrame();
+    method public int getImageContentType();
+    method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getStartKeyFrame();
+    method public int getStatusCode();
+    method @NonNull public String getTaskId();
+    method @NonNull public java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh> getTexturedMeshes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2; // 0x2
+    field public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3; // 0x3
+    field public static final int CINEMATIC_EFFECT_STATUS_OK = 1; // 0x1
+    field public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4; // 0x4
+    field public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5; // 0x5
+    field public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0; // 0x0
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectResponse> CREATOR;
+    field public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2; // 0x2
+    field public static final int IMAGE_CONTENT_TYPE_OTHER = 3; // 0x3
+    field public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1; // 0x1
+    field public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class CinematicEffectResponse.Builder {
+    ctor public CinematicEffectResponse.Builder(int, @NonNull String);
+    method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse build();
+    method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setEndKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes);
+    method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setImageContentType(int);
+    method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setStartKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes);
+    method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setTexturedMeshes(@NonNull java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh>);
+  }
+
+  public final class TexturedMesh implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.graphics.Bitmap getBitmap();
+    method @NonNull public int[] getIndices();
+    method @NonNull public int getIndicesLayoutType();
+    method @NonNull public float[] getVertices();
+    method @NonNull public int getVerticesLayoutType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.TexturedMesh> CREATOR;
+    field public static final int INDICES_LAYOUT_TRIANGLES = 1; // 0x1
+    field public static final int INDICES_LAYOUT_UNDEFINED = 0; // 0x0
+    field public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1; // 0x1
+    field public static final int VERTICES_LAYOUT_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class TexturedMesh.Builder {
+    ctor public TexturedMesh.Builder(@NonNull android.graphics.Bitmap);
+    method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh build();
+    method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndices(@NonNull int[]);
+    method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndicesLayoutType(int);
+    method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVertices(@NonNull float[]);
+    method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVerticesLayoutType(int);
+  }
+
+  public final class WallpaperEffectsGenerationManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION) public void generateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager.CinematicEffectListener);
+  }
+
+  public static interface WallpaperEffectsGenerationManager.CinematicEffectListener {
+    method public void onCinematicEffectGenerated(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse);
+  }
+
+}
+
 package android.apphibernation {
 
   public class AppHibernationManager {
@@ -2742,6 +2858,7 @@
     field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
     field public static final String UWB_SERVICE = "uwb";
     field public static final String VR_SERVICE = "vrmanager";
+    field public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = "wallpaper_effects_generation";
     field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
     field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -3713,6 +3830,7 @@
     method public void sendKeyEvent(int, boolean);
     method public void sendVendorCommand(int, byte[], boolean);
     method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener);
+    method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int);
   }
 
   public static interface HdmiClient.OnDeviceSelectedListener {
@@ -9017,6 +9135,7 @@
   }
 
   public static class Build.VERSION {
+    field @NonNull public static final java.util.Set<java.lang.String> KNOWN_CODENAMES;
     field @NonNull public static final String PREVIEW_SDK_FINGERPRINT;
   }
 
@@ -10294,6 +10413,8 @@
   }
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+    method public static int getIntForUser(@NonNull android.content.ContentResolver, @NonNull String, int, int);
+    method @Nullable public static String getStringForUser(@NonNull android.content.ContentResolver, @NonNull String, int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, @Nullable String, boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
     field @Deprecated public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled";
@@ -10645,6 +10766,8 @@
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback);
     method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback);
+    method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityCallback);
+    method public void onStopProximityUpdates();
     field public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; // 0x6
     field public static final int ATTENTION_FAILURE_CANCELLED = 3; // 0x3
     field public static final int ATTENTION_FAILURE_PREEMPTED = 4; // 0x4
@@ -10652,6 +10775,7 @@
     field public static final int ATTENTION_FAILURE_UNKNOWN = 2; // 0x2
     field public static final int ATTENTION_SUCCESS_ABSENT = 0; // 0x0
     field public static final int ATTENTION_SUCCESS_PRESENT = 1; // 0x1
+    field public static final double PROXIMITY_UNKNOWN = -1.0;
     field public static final String SERVICE_INTERFACE = "android.service.attention.AttentionService";
   }
 
@@ -10660,6 +10784,10 @@
     method public void onSuccess(int, long);
   }
 
+  public static final class AttentionService.ProximityCallback {
+    method public void onProximityUpdate(double);
+  }
+
 }
 
 package android.service.autofill {
@@ -11083,6 +11211,7 @@
     method public void onCreate();
     method public void onDestroy();
     method public void onGameTaskFocusChanged(boolean);
+    method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame();
     method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
     method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
@@ -11512,6 +11641,7 @@
     method @Deprecated public final void grantTrust(CharSequence, long, boolean);
     method public final void grantTrust(CharSequence, long, int);
     method public final void isEscrowTokenActive(long, android.os.UserHandle);
+    method public final void lockUser();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public boolean onConfigure(java.util.List<android.os.PersistableBundle>);
     method public void onDeviceLocked();
@@ -11522,13 +11652,16 @@
     method public void onEscrowTokenStateReceived(long, int);
     method public void onTrustTimeout();
     method public void onUnlockAttempt(boolean);
+    method public void onUserRequestedUnlock();
     method public final void removeEscrowToken(long, android.os.UserHandle);
     method public final void revokeTrust();
     method public final void setManagingTrust(boolean);
     method public final void showKeyguardErrorMessage(@NonNull CharSequence);
     method public final void unlockUserWithToken(long, byte[], android.os.UserHandle);
     field public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 2; // 0x2
+    field public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 8; // 0x8
     field public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1; // 0x1
+    field public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 4; // 0x4
     field public static final String SERVICE_INTERFACE = "android.service.trust.TrustAgentService";
     field public static final int TOKEN_STATE_ACTIVE = 1; // 0x1
     field public static final int TOKEN_STATE_INACTIVE = 0; // 0x0
@@ -11704,6 +11837,17 @@
 
 }
 
+package android.service.wallpapereffectsgeneration {
+
+  public abstract class WallpaperEffectsGenerationService extends android.app.Service {
+    ctor public WallpaperEffectsGenerationService();
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onGenerateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest);
+    method public final void returnCinematicEffectResponse(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse);
+  }
+
+}
+
 package android.service.watchdog {
 
   public abstract class ExplicitHealthCheckService extends android.app.Service {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 52a180b..fea7396 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -278,6 +278,7 @@
   }
 
   public final class GameManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public boolean isAngleEnabled(@NonNull String);
     method public void setGameServiceProvider(@Nullable String);
   }
 
@@ -497,6 +498,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
     method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
     method @NonNull public static String operationSafetyReasonToString(int);
     method @NonNull public static String operationToString(int);
@@ -800,6 +802,7 @@
     method @NonNull public String getPermissionControllerPackageName();
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+    method @NonNull public String getSupplementalProcessPackageName();
     method @Nullable public String getSystemTextClassifierPackageName();
     method @Nullable public String getWellbeingPackageName();
     method public void holdLock(android.os.IBinder, int);
@@ -1738,8 +1741,11 @@
 
   public final class Parcel {
     method public boolean allowSquashing();
+    method public int getFlags();
     method public int readExceptionCode();
     method public void restoreAllowSquashing(boolean);
+    field public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1; // 0x1
+    field public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 2; // 0x2
   }
 
   public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -2738,6 +2744,7 @@
     method @NonNull public android.view.Display.Mode getDefaultMode();
     method @NonNull public int[] getReportedHdrTypes();
     method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
+    method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode();
     method public int getType();
     method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean hasAccess(int);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 6b0aef8..42d2d28 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -571,6 +571,31 @@
      */
     public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15;
 
+    /**
+     * Action to trigger dpad up keyevent.
+     */
+    public static final int GLOBAL_ACTION_DPAD_UP = 16;
+
+    /**
+     * Action to trigger dpad down keyevent.
+     */
+    public static final int GLOBAL_ACTION_DPAD_DOWN = 17;
+
+    /**
+     * Action to trigger dpad left keyevent.
+     */
+    public static final int GLOBAL_ACTION_DPAD_LEFT = 18;
+
+    /**
+     * Action to trigger dpad right keyevent.
+     */
+    public static final int GLOBAL_ACTION_DPAD_RIGHT = 19;
+
+    /**
+     * Action to trigger dpad center keyevent.
+     */
+    public static final int GLOBAL_ACTION_DPAD_CENTER = 20;
+
     private static final String LOG_TAG = "AccessibilityService";
 
     /**
@@ -2328,10 +2353,16 @@
      * @param action The action to perform.
      * @return Whether the action was successfully performed.
      *
+     * Perform actions using ids like the id constants referenced below:
      * @see #GLOBAL_ACTION_BACK
      * @see #GLOBAL_ACTION_HOME
      * @see #GLOBAL_ACTION_NOTIFICATIONS
      * @see #GLOBAL_ACTION_RECENTS
+     * @see #GLOBAL_ACTION_DPAD_UP
+     * @see #GLOBAL_ACTION_DPAD_DOWN
+     * @see #GLOBAL_ACTION_DPAD_LEFT
+     * @see #GLOBAL_ACTION_DPAD_RIGHT
+     * @see #GLOBAL_ACTION_DPAD_CENTER
      */
     public final boolean performGlobalAction(int action) {
         IAccessibilityServiceConnection connection =
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b1956ef..20ffa25 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -863,6 +863,18 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public String getSupplementalProcessPackageName() {
+        try {
+            return mPM.getSupplementalProcessPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     @Override
     public boolean addPermission(PermissionInfo info) {
         return getPermissionManager().addPermission(info, false);
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 289b348..040399e 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -181,14 +181,18 @@
     /**
      * Returns if ANGLE is enabled for a given package and user ID.
      * <p>
+     * ANGLE (Almost Native Graphics Layer Engine) can translate OpenGL ES commands to Vulkan
+     * commands. Enabling ANGLE may improve the performance and/or reduce the power consumption of
+     * applications.
      * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
      *
      * @hide
      */
+    @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
-    public @GameMode boolean getAngleEnabled(@NonNull String packageName) {
+    public @GameMode boolean isAngleEnabled(@NonNull String packageName) {
         try {
-            return mService.getAngleEnabled(packageName, mContext.getUserId());
+            return mService.isAngleEnabled(packageName, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 3ea07676..7035ac0 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -26,7 +26,7 @@
     int getGameMode(String packageName, int userId);
     void setGameMode(String packageName, int gameMode, int userId);
     int[] getAvailableGameModes(String packageName);
-    boolean getAngleEnabled(String packageName, int userId);
+    boolean isAngleEnabled(String packageName, int userId);
     void setGameState(String packageName, in GameState gameState, int userId);
     GameModeInfo getGameModeInfo(String packageName, int userId);
     void setGameServiceProvider(String packageName);
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 56c301f..8fcb07f 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +28,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.drawable.Icon;
+import android.media.MediaRoute2Info;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -39,6 +41,7 @@
 
 import com.android.internal.statusbar.IAddTileResultCallback;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
 import com.android.internal.statusbar.NotificationVisibility;
 
 import java.lang.annotation.Retention;
@@ -338,6 +341,166 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface NavBarModeOverride {}
 
+    /**
+     * State indicating that this sender device is close to a receiver device, so the user can
+     * potentially *start* a cast to the receiver device if the user moves their device a bit
+     * closer.
+     * <p>
+     * Important notes:
+     * <ul>
+     *     <li>This state represents that the device is close enough to inform the user that
+     *     transferring is an option, but the device is *not* close enough to actually initiate a
+     *     transfer yet.</li>
+     *     <li>This state is for *starting* a cast. It should be used when this device is currently
+     *     playing media locally and the media should be transferred to be played on the receiver
+     *     device instead.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0;
+
+    /**
+     * State indicating that this sender device is close to a receiver device, so the user can
+     * potentially *end* a cast on the receiver device if the user moves this device a bit closer.
+     * <p>
+     * Important notes:
+     * <ul>
+     *     <li>This state represents that the device is close enough to inform the user that
+     *     transferring is an option, but the device is *not* close enough to actually initiate a
+     *     transfer yet.</li>
+     *     <li>This state is for *ending* a cast. It should be used when media is currently being
+     *     played on the receiver device and the media should be transferred to play locally
+     *     instead.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1;
+
+    /**
+     * State indicating that a media transfer from this sender device to a receiver device has been
+     * started.
+     * <p>
+     * Important note: This state is for *starting* a cast. It should be used when this device is
+     * currently playing media locally and the media has started being transferred to the receiver
+     * device instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2;
+
+    /**
+     * State indicating that a media transfer from the receiver and back to this sender device
+     * has been started.
+     * <p>
+     * Important note: This state is for *ending* a cast. It should be used when media is currently
+     * being played on the receiver device and the media has started being transferred to play
+     * locally instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3;
+
+    /**
+     * State indicating that a media transfer from this sender device to a receiver device has
+     * finished successfully.
+     * <p>
+     * Important note: This state is for *starting* a cast. It should be used when this device had
+     * previously been playing media locally and the media has successfully been transferred to the
+     * receiver device instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4;
+
+    /**
+     * State indicating that a media transfer from the receiver and back to this sender device has
+     * finished successfully.
+     * <p>
+     * Important note: This state is for *ending* a cast. It should be used when media was
+     * previously being played on the receiver device and has been successfully transferred to play
+     * locally on this device instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5;
+
+    /**
+     * State indicating that the attempted transfer to the receiver device has failed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6;
+
+    /**
+     * State indicating that the attempted transfer back to this device has failed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7;
+
+    /**
+     * State indicating that this sender device is no longer close to the receiver device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8;
+
+    /** @hide */
+    @IntDef(prefix = {"MEDIA_TRANSFER_SENDER_STATE_"}, value = {
+            MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+            MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+            MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+            MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+            MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
+            MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaTransferSenderState {}
+
+    /**
+     * State indicating that this receiver device is close to a sender device, so the user can
+     * potentially start or end a cast to the receiver device if the user moves the sender device a
+     * bit closer.
+     * <p>
+     * Important note: This state represents that the device is close enough to inform the user that
+     * transferring is an option, but the device is *not* close enough to actually initiate a
+     * transfer yet.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0;
+
+    /**
+     * State indicating that this receiver device is no longer close to the sender device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1;
+
+    /** @hide */
+    @IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = {
+            MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+            MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaTransferReceiverState {}
+
     @UnsupportedAppUsage
     private Context mContext;
     private IStatusBarService mService;
@@ -789,6 +952,81 @@
         return navBarModeOverride;
     }
 
+    /**
+     * Notifies the system of a new media tap-to-transfer state for the <b>sender</b> device.
+     *
+     * <p>The callback should only be provided for the {@link
+     * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED} or {@link
+     * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED} states, since those are the
+     * only states where an action can be un-done.
+     *
+     * @param displayState the new state for media tap-to-transfer.
+     * @param routeInfo the media route information for the media being transferred.
+     * @param undoExecutor an executor to run the callback on and must be provided if the
+     *                     callback is non-null.
+     * @param undoCallback a callback that will be triggered if the user elects to undo a media
+     *                     transfer.
+     *
+     * @throws IllegalArgumentException if an undo callback is provided for states that are not a
+     *   succeeded state.
+     * @throws IllegalArgumentException if an executor is not provided when a callback is.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void updateMediaTapToTransferSenderDisplay(
+            @MediaTransferSenderState int displayState,
+            @NonNull MediaRoute2Info routeInfo,
+            @Nullable Executor undoExecutor,
+            @Nullable Runnable undoCallback
+    ) {
+        Objects.requireNonNull(routeInfo);
+        if (displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED
+                && displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED
+                && undoCallback != null) {
+            throw new IllegalArgumentException(
+                    "The undoCallback should only be provided when the state is a "
+                            + "transfer succeeded state");
+        }
+        if (undoCallback != null && undoExecutor == null) {
+            throw new IllegalArgumentException(
+                    "You must pass an executor when you pass an undo callback");
+        }
+        IStatusBarService svc = getService();
+        try {
+            UndoCallback callbackProxy = null;
+            if (undoExecutor != null) {
+                callbackProxy = new UndoCallback(undoExecutor, undoCallback);
+            }
+            svc.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, callbackProxy);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the system of a new media tap-to-transfer state for the <b>receiver</b> device.
+     *
+     * @param displayState the new state for media tap-to-transfer.
+     * @param routeInfo the media route information for the media being transferred.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void updateMediaTapToTransferReceiverDisplay(
+            @MediaTransferReceiverState int displayState,
+            @NonNull MediaRoute2Info routeInfo) {
+        Objects.requireNonNull(routeInfo);
+        IStatusBarService svc = getService();
+        try {
+            svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String windowStateToString(int state) {
         if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
@@ -1071,4 +1309,29 @@
             mExecutor.execute(() -> mCallback.accept(userResponse));
         }
     }
+
+    /**
+     * @hide
+     */
+    static final class UndoCallback extends IUndoMediaTransferCallback.Stub {
+        @NonNull
+        private final Executor mExecutor;
+        @NonNull
+        private final Runnable mCallback;
+
+        UndoCallback(@NonNull Executor executor, @NonNull Runnable callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onUndoTriggered() {
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(mCallback);
+            } finally {
+                restoreCallingIdentity(callingIdentity);
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bbdd705..79180cb 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -51,6 +51,8 @@
 import android.app.usage.NetworkStatsManager;
 import android.app.usage.StorageStatsManager;
 import android.app.usage.UsageStatsManager;
+import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
+import android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager;
 import android.apphibernation.AppHibernationManager;
 import android.appwidget.AppWidgetManager;
 import android.bluetooth.BluetoothFrameworkInitializer;
@@ -1297,6 +1299,20 @@
                     }
                 });
 
+        registerService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE,
+                WallpaperEffectsGenerationManager.class,
+                new CachedServiceFetcher<WallpaperEffectsGenerationManager>() {
+                    @Override
+                    public WallpaperEffectsGenerationManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getService(
+                                Context.WALLPAPER_EFFECTS_GENERATION_SERVICE);
+                        return b == null ? null :
+                                new WallpaperEffectsGenerationManager(
+                                        IWallpaperEffectsGenerationManager.Stub.asInterface(b));
+                    }
+                });
+
         registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
             @Override
             public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a4227a4..8326580 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3556,7 +3556,8 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void acknowledgeNewUserDisclaimer() {
         if (mService != null) {
@@ -3569,6 +3570,25 @@
     }
 
     /**
+     * Checks whether the new managed user disclaimer was viewed by the current user.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    @TestApi
+    public boolean isNewUserDisclaimerAcknowledged() {
+        if (mService != null) {
+            try {
+                return mService.isNewUserDisclaimerAcknowledged();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -10146,7 +10166,7 @@
      * @hide
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.CREATE_USERS})
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public @UserOperationResult int logoutUser() {
         // TODO(b/214336184): add CTS test
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 927ee0c..a7a51f8 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -272,6 +272,7 @@
     int getLogoutUserId();
     List<UserHandle> getSecondaryUsers(in ComponentName who);
     void acknowledgeNewUserDisclaimer();
+    boolean isNewUserDisclaimerAcknowledged();
 
     void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
diff --git a/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java b/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java
new file mode 100644
index 0000000..dfbc7a4
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Representing the position and other parameters of camera of a single frame.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CameraAttributes implements Parcelable {
+    /**
+     * The location of the anchor within the 3D scene.
+     * Expecting 3 floats representing the x, y, z coordinates
+     * of the anchor point.
+     */
+    @NonNull
+    private float[] mAnchorPointInWorldSpace;
+    /**
+     * Where the anchor point should project to in the output image.
+     * Expecting 2 floats representing the u,v coordinates of the
+     * anchor point.
+     */
+    @NonNull
+    private float[] mAnchorPointInOutputUvSpace;
+    /**
+     * Specifies the amount of yaw orbit rotation the camera should perform
+     * around the anchor point in world space.
+     */
+    private float mCameraOrbitYawDegrees;
+    /**
+     * Specifies the amount of pitch orbit rotation the camera should perform
+     * around the anchor point in world space.
+     */
+    private float mCameraOrbitPitchDegrees;
+    /**
+     * Specifies by how much the camera should be placed towards the anchor
+     * point in world space, which is also called dolly distance.
+     */
+    private float mDollyDistanceInWorldSpace;
+    /**
+     * Specifies the vertical fov degrees of the virtual image.
+     */
+    private float mVerticalFovDegrees;
+    /**
+     * The frustum of near plane.
+     */
+    private float mFrustumNearInWorldSpace;
+    /**
+     * The frustum of far plane.
+     */
+    private float mFrustumFarInWorldSpace;
+
+    private CameraAttributes(Parcel in) {
+        this.mCameraOrbitYawDegrees = in.readFloat();
+        this.mCameraOrbitPitchDegrees = in.readFloat();
+        this.mDollyDistanceInWorldSpace = in.readFloat();
+        this.mVerticalFovDegrees = in.readFloat();
+        this.mFrustumNearInWorldSpace = in.readFloat();
+        this.mFrustumFarInWorldSpace = in.readFloat();
+        this.mAnchorPointInWorldSpace = in.createFloatArray();
+        this.mAnchorPointInOutputUvSpace = in.createFloatArray();
+    }
+
+    private CameraAttributes(float[] anchorPointInWorldSpace, float[] anchorPointInOutputUvSpace,
+            float cameraOrbitYawDegrees, float cameraOrbitPitchDegrees,
+            float dollyDistanceInWorldSpace,
+            float verticalFovDegrees, float frustumNearInWorldSpace, float frustumFarInWorldSpace) {
+        mAnchorPointInWorldSpace = anchorPointInWorldSpace;
+        mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace;
+        mCameraOrbitYawDegrees = cameraOrbitYawDegrees;
+        mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees;
+        mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace;
+        mVerticalFovDegrees = verticalFovDegrees;
+        mFrustumNearInWorldSpace = frustumNearInWorldSpace;
+        mFrustumFarInWorldSpace = frustumFarInWorldSpace;
+    }
+
+    /**
+     * Get the location of the anchor within the 3D scene. The response float array contains
+     * 3 floats representing the x, y, z coordinates
+     */
+    @NonNull
+    public float[] getAnchorPointInWorldSpace() {
+        return mAnchorPointInWorldSpace;
+    }
+
+    /**
+     * Get where the anchor point should project to in the output image. The response float
+     * array contains 2 floats representing the u,v coordinates of the anchor point.
+     */
+    @NonNull
+    public float[] getAnchorPointInOutputUvSpace() {
+        return mAnchorPointInOutputUvSpace;
+    }
+
+    /**
+     * Get the camera yaw orbit rotation.
+     */
+    public float getCameraOrbitYawDegrees() {
+        return mCameraOrbitYawDegrees;
+    }
+
+    /**
+     * Get the camera pitch orbit rotation.
+     */
+    public float getCameraOrbitPitchDegrees() {
+        return mCameraOrbitPitchDegrees;
+    }
+
+    /**
+     * Get how many units the camera should be placed towards the anchor point in world space.
+     */
+    public float getDollyDistanceInWorldSpace() {
+        return mDollyDistanceInWorldSpace;
+    }
+
+    /**
+     * Get the camera vertical fov degrees.
+     */
+    public float getVerticalFovDegrees() {
+        return mVerticalFovDegrees;
+    }
+
+    /**
+     * Get the frustum in near plane.
+     */
+    public float getFrustumNearInWorldSpace() {
+        return mFrustumNearInWorldSpace;
+    }
+
+    /**
+     * Get the frustum in far plane.
+     */
+    public float getFrustumFarInWorldSpace() {
+        return mFrustumFarInWorldSpace;
+    }
+
+    @NonNull
+    public static final Creator<CameraAttributes> CREATOR = new Creator<CameraAttributes>() {
+        @Override
+        public CameraAttributes createFromParcel(Parcel in) {
+            return new CameraAttributes(in);
+        }
+
+        @Override
+        public CameraAttributes[] newArray(int size) {
+            return new CameraAttributes[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeFloat(mCameraOrbitYawDegrees);
+        out.writeFloat(mCameraOrbitPitchDegrees);
+        out.writeFloat(mDollyDistanceInWorldSpace);
+        out.writeFloat(mVerticalFovDegrees);
+        out.writeFloat(mFrustumNearInWorldSpace);
+        out.writeFloat(mFrustumFarInWorldSpace);
+        out.writeFloatArray(mAnchorPointInWorldSpace);
+        out.writeFloatArray(mAnchorPointInOutputUvSpace);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Builder for {@link CameraAttributes}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        @NonNull
+        private float[] mAnchorPointInWorldSpace;
+        @NonNull
+        private float[] mAnchorPointInOutputUvSpace;
+        private float mCameraOrbitYawDegrees;
+        private float mCameraOrbitPitchDegrees;
+        private float mDollyDistanceInWorldSpace;
+        private float mVerticalFovDegrees;
+        private float mFrustumNearInWorldSpace;
+        private float mFrustumFarInWorldSpace;
+
+        /**
+         * Constructor with anchor point in world space and anchor point in output image
+         * space.
+         *
+         * @param anchorPointInWorldSpace the location of the anchor within the 3D scene. The
+         *  float array contains 3 floats representing the x, y, z coordinates.
+         * @param anchorPointInOutputUvSpace where the anchor point should project to in the
+         *  output image. The  float array contains 2 floats representing the u,v coordinates
+         *  of the anchor point.
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder(@NonNull float[] anchorPointInWorldSpace,
+                @NonNull float[] anchorPointInOutputUvSpace) {
+            mAnchorPointInWorldSpace = anchorPointInWorldSpace;
+            mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace;
+        }
+
+        /**
+         * Sets the camera orbit yaw rotation.
+         */
+        @NonNull
+        public Builder setCameraOrbitYawDegrees(
+                @FloatRange(from = -180.0f, to = 180.0f) float cameraOrbitYawDegrees) {
+            mCameraOrbitYawDegrees = cameraOrbitYawDegrees;
+            return this;
+        }
+
+        /**
+         * Sets the camera orbit pitch rotation.
+         */
+        @NonNull
+        public Builder setCameraOrbitPitchDegrees(
+                @FloatRange(from = -90.0f, to = 90.0f) float cameraOrbitPitchDegrees) {
+            mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees;
+            return this;
+        }
+
+        /**
+         * Sets the camera dolly distance.
+         */
+        @NonNull
+        public Builder setDollyDistanceInWorldSpace(float dollyDistanceInWorldSpace) {
+            mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace;
+            return this;
+        }
+
+        /**
+         * Sets the camera vertical fov degree.
+         */
+        @NonNull
+        public Builder setVerticalFovDegrees(
+                @FloatRange(from = 0.0f, to = 180.0f, fromInclusive = false)
+                        float verticalFovDegrees) {
+            mVerticalFovDegrees = verticalFovDegrees;
+            return this;
+        }
+
+        /**
+         * Sets the frustum in near plane.
+         */
+        @NonNull
+        public Builder setFrustumNearInWorldSpace(
+                @FloatRange(from = 0.0f) float frustumNearInWorldSpace) {
+            mFrustumNearInWorldSpace = frustumNearInWorldSpace;
+            return this;
+        }
+
+        /**
+         * Sets the frustum in far plane.
+         */
+        @NonNull
+        public Builder setFrustumFarInWorldSpace(
+                @FloatRange(from = 0.0f) float frustumFarInWorldSpace) {
+            mFrustumFarInWorldSpace = frustumFarInWorldSpace;
+            return this;
+        }
+
+        /**
+         * Builds a new {@link CameraAttributes} instance.
+         */
+        @NonNull
+        public CameraAttributes build() {
+            return new CameraAttributes(mAnchorPointInWorldSpace,
+                    mAnchorPointInOutputUvSpace,
+                    mCameraOrbitYawDegrees,
+                    mCameraOrbitPitchDegrees,
+                    mDollyDistanceInWorldSpace,
+                    mVerticalFovDegrees,
+                    mFrustumNearInWorldSpace,
+                    mFrustumFarInWorldSpace);
+        }
+    }
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl
similarity index 79%
rename from packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
rename to core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl
index cb602d79..2347746 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2021, The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.app.wallpapereffectsgeneration;
 
-parcelable NetworkStateSnapshot;
+parcelable CinematicEffectRequest;
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java
new file mode 100644
index 0000000..f2e3313
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link CinematicEffectRequest} is the data class having all the information
+ * passed to wallpaper effects generation service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CinematicEffectRequest implements Parcelable {
+    /**
+     * Unique id of a cienmatic effect generation task.
+     */
+    @NonNull
+    private String mTaskId;
+
+    /**
+     * The bitmap to generate cinematic effect from.
+     */
+    @NonNull
+    private Bitmap mBitmap;
+
+    private CinematicEffectRequest(Parcel in) {
+        this.mTaskId = in.readString();
+        this.mBitmap = Bitmap.CREATOR.createFromParcel(in);
+    }
+
+    /**
+     * Constructor with task id and bitmap.
+     */
+    public CinematicEffectRequest(@NonNull String taskId, @NonNull Bitmap bitmap) {
+        mTaskId = taskId;
+        mBitmap = bitmap;
+    }
+
+    /**
+     * Returns the task id.
+     */
+    @NonNull
+    public String getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Returns the bitmap of this request.
+     */
+    @NonNull
+    public Bitmap getBitmap() {
+        return mBitmap;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        CinematicEffectRequest that = (CinematicEffectRequest) o;
+        return mTaskId.equals(that.mTaskId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTaskId);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mTaskId);
+        mBitmap.writeToParcel(out, flags);
+    }
+
+    @NonNull
+    public static final Creator<CinematicEffectRequest> CREATOR =
+            new Creator<CinematicEffectRequest>() {
+                @Override
+                public CinematicEffectRequest createFromParcel(Parcel in) {
+                    return new CinematicEffectRequest(in);
+                }
+
+                @Override
+                public CinematicEffectRequest[] newArray(int size) {
+                    return new CinematicEffectRequest[size];
+                }
+            };
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl
similarity index 79%
copy from packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
copy to core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl
index cb602d79..1bd1e1e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2021, The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net;
+ package android.app.wallpapereffectsgeneration;
 
-parcelable NetworkStateSnapshot;
+ parcelable CinematicEffectResponse;
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java
new file mode 100644
index 0000000..1254794
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link CinematicEffectResponse} include textured meshes
+ * and camera attributes of key frames.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CinematicEffectResponse implements Parcelable {
+    /** @hide */
+    @IntDef(prefix = {"CINEMATIC_EFFECT_STATUS_"},
+            value = {CINEMATIC_EFFECT_STATUS_UNKNOWN,
+                    CINEMATIC_EFFECT_STATUS_OK,
+                    CINEMATIC_EFFECT_STATUS_ERROR,
+                    CINEMATIC_EFFECT_STATUS_NOT_READY,
+                    CINEMATIC_EFFECT_STATUS_PENDING,
+                    CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CinematicEffectStatusCode {}
+
+    /** Cinematic effect generation unknown status. */
+    public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0;
+    /** Cinematic effect generation success. */
+    public static final int CINEMATIC_EFFECT_STATUS_OK = 1;
+    /** Cinematic effect generation failure. */
+    public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2;
+    /** Service not ready for cinematic effect generation. */
+    public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3;
+    /** Cienmatic effect generation process is pending. */
+    public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4;
+    /** Too manay requests for server to handle. */
+    public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5;
+
+    /** @hide */
+    @IntDef(prefix = {"IMAGE_CONTENT_TYPE_"},
+            value = {IMAGE_CONTENT_TYPE_UNKNOWN,
+                    IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT,
+                    IMAGE_CONTENT_TYPE_LANDSCAPE,
+                    IMAGE_CONTENT_TYPE_OTHER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImageContentType {}
+
+    /** Image content unknown. */
+    public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0;
+    /** Image content is people portrait. */
+    public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1;
+    /** Image content is landscape. */
+    public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2;
+    /** Image content is doesn't belong to other types. */
+    public static final int IMAGE_CONTENT_TYPE_OTHER = 3;
+
+
+    @CinematicEffectStatusCode
+    private int mStatusCode;
+
+    /** The id of the cinematic effect generation task. */
+    @NonNull
+    private String mTaskId;
+
+    @ImageContentType
+    private int mImageContentType;
+
+    /** The textured mesh required to render cinematic effect. */
+    @NonNull
+    private List<TexturedMesh> mTexturedMeshes;
+
+    /** The start camera position for animation. */
+    @Nullable
+    private CameraAttributes mStartKeyFrame;
+
+    /** The end camera position for animation. */
+    @Nullable
+    private CameraAttributes mEndKeyFrame;
+
+    private CinematicEffectResponse(Parcel in) {
+        mStatusCode = in.readInt();
+        mTaskId = in.readString();
+        mImageContentType = in.readInt();
+        mTexturedMeshes = new ArrayList<TexturedMesh>();
+        in.readTypedList(mTexturedMeshes, TexturedMesh.CREATOR);
+        mStartKeyFrame = in.readTypedObject(CameraAttributes.CREATOR);
+        mEndKeyFrame = in.readTypedObject(CameraAttributes.CREATOR);
+    }
+
+    private CinematicEffectResponse(@CinematicEffectStatusCode int statusCode,
+            String taskId,
+            @ImageContentType int imageContentType,
+            List<TexturedMesh> texturedMeshes,
+            CameraAttributes startKeyFrame,
+            CameraAttributes endKeyFrame) {
+        mStatusCode = statusCode;
+        mTaskId = taskId;
+        mImageContentType = imageContentType;
+        mStartKeyFrame = startKeyFrame;
+        mEndKeyFrame = endKeyFrame;
+        mTexturedMeshes = texturedMeshes;
+    }
+
+    /** Gets the cinematic effect generation status code. */
+    @CinematicEffectStatusCode
+    public int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /** Get the task id. */
+    @NonNull
+    public String getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Get the image content type, which briefly classifies what's
+     * the content of image, like people portrait, landscape etc.
+     */
+    @ImageContentType
+    public int getImageContentType() {
+        return mImageContentType;
+    }
+
+    /** Get the textured meshes. */
+    @NonNull
+    public List<TexturedMesh> getTexturedMeshes() {
+        return mTexturedMeshes;
+    }
+
+    /**
+     * Get the camera attributes (position info and other parameters, see docs of
+     * {@link CameraAttributes}) of the start key frame on the animation path.
+     */
+    @Nullable
+    public CameraAttributes getStartKeyFrame() {
+        return mStartKeyFrame;
+    }
+
+    /**
+     * Get the camera attributes (position info and other parameters, see docs of
+     * {@link CameraAttributes}) of the end key frame on the animation path.
+     */
+    @Nullable
+    public CameraAttributes getEndKeyFrame() {
+        return mEndKeyFrame;
+    }
+
+    @NonNull
+    public static final Creator<CinematicEffectResponse> CREATOR =
+            new Creator<CinematicEffectResponse>() {
+                @Override
+                public CinematicEffectResponse createFromParcel(Parcel in) {
+                    return new CinematicEffectResponse(in);
+                }
+
+                @Override
+                public CinematicEffectResponse[] newArray(int size) {
+                    return new CinematicEffectResponse[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStatusCode);
+        out.writeString(mTaskId);
+        out.writeInt(mImageContentType);
+        out.writeTypedList(mTexturedMeshes, flags);
+        out.writeTypedObject(mStartKeyFrame, flags);
+        out.writeTypedObject(mEndKeyFrame, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        CinematicEffectResponse that = (CinematicEffectResponse) o;
+        return mTaskId.equals(that.mTaskId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTaskId);
+    }
+    /**
+     * Builder of {@link CinematicEffectResponse}
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        @CinematicEffectStatusCode
+        private int mStatusCode;
+        @NonNull
+        private String mTaskId;
+        @ImageContentType
+        private int mImageContentType;
+        @NonNull
+        private List<TexturedMesh> mTexturedMeshes;
+        @Nullable
+        private CameraAttributes mStartKeyFrame;
+        @Nullable
+        private CameraAttributes mEndKeyFrame;
+
+        /**
+         * Constructor with task id and status code.
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder(@CinematicEffectStatusCode int statusCode, @NonNull String taskId) {
+            mStatusCode = statusCode;
+            mTaskId = taskId;
+        }
+
+        /**
+         * Sets the image content type.
+         */
+        @NonNull
+        public Builder setImageContentType(@ImageContentType int imageContentType) {
+            mImageContentType = imageContentType;
+            return this;
+        }
+
+
+        /**
+         * Sets the textured meshes.
+         */
+        @NonNull
+        public Builder setTexturedMeshes(@NonNull List<TexturedMesh> texturedMeshes) {
+            mTexturedMeshes = texturedMeshes;
+            return this;
+        }
+
+        /**
+         * Sets start key frame.
+         */
+        @NonNull
+        public Builder setStartKeyFrame(@Nullable CameraAttributes startKeyFrame) {
+            mStartKeyFrame = startKeyFrame;
+            return this;
+        }
+
+        /**
+         * Sets end key frame.
+         */
+        @NonNull
+        public Builder setEndKeyFrame(@Nullable CameraAttributes endKeyFrame) {
+            mEndKeyFrame = endKeyFrame;
+            return this;
+        }
+
+        /**
+         * Builds a {@link CinematicEffectResponse} instance.
+         */
+        @NonNull
+        public CinematicEffectResponse build() {
+            if (mTexturedMeshes == null) {
+                // Place holder because build doesn't allow list to be nullable.
+                mTexturedMeshes = new ArrayList<>(0);
+            }
+            return new CinematicEffectResponse(mStatusCode, mTaskId, mImageContentType,
+                    mTexturedMeshes, mStartKeyFrame, mEndKeyFrame);
+        }
+    }
+}
diff --git a/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl b/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl
new file mode 100644
index 0000000..c1a698d
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+
+
+/**
+ * Callback used by system server to notify invoker of {@link WallpaperEffectsGenerationMAnager}
+ * of the cinematic effect generation result.
+ *
+ * @hide
+ */
+oneway interface ICinematicEffectListener {
+  void onCinematicEffectGenerated(in CinematicEffectResponse response);
+}
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl b/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl
new file mode 100644
index 0000000..706a89c
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
+
+/**
+ * Used by {@link android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager}
+ * to to generate effects.
+ *
+ * @hide
+ */
+oneway interface IWallpaperEffectsGenerationManager {
+  void generateCinematicEffect(in CinematicEffectRequest request,
+        in ICinematicEffectListener listener);
+
+  void returnCinematicEffectResponse(in CinematicEffectResponse response);
+}
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java b/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java
new file mode 100644
index 0000000..630de45
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The textured mesh representation, including a texture (bitmap) to sample from when rendering,
+ * and a mesh consisting of primitives such as triangles. The mesh is represented by an indices
+ * array describing the set of primitives in the mesh, and a vertices array that the indices
+ * refer to.
+ *
+ * @hide
+ */
+@SystemApi
+public final class TexturedMesh implements Parcelable {
+    /**
+     * The texture to sample from when rendering mesh.
+     */
+    @NonNull
+    private Bitmap mBitmap;
+
+    /**
+     * The set of primitives as pointers into the vertices.
+     */
+    @NonNull
+    private int[] mIndices;
+
+    /**
+     * The specific vertices that the indices refer to.
+     */
+    @NonNull
+    private float[] mVertices;
+
+    /** @hide */
+    @IntDef(prefix = {"INDICES_LAYOUT_"}, value = {
+            INDICES_LAYOUT_UNDEFINED,
+            INDICES_LAYOUT_TRIANGLES})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndicesLayoutType {
+    }
+
+    /** Undefined indices layout */
+    public static final int INDICES_LAYOUT_UNDEFINED = 0;
+    /**
+     * Indices layout is triangle. Vertices are grouped into 3 and each
+     * group forms a triangle.
+     */
+    public static final int INDICES_LAYOUT_TRIANGLES = 1;
+
+    @IndicesLayoutType
+    private int mIndicesLayoutType;
+
+    /** @hide */
+    @IntDef(prefix = {"VERTICES_LAYOUT_"}, value = {
+            VERTICES_LAYOUT_UNDEFINED,
+            VERTICES_LAYOUT_POSITION3_UV2})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VerticesLayoutType {
+    }
+
+    /**
+     * Undefined vertices layout.
+     */
+    public static final int VERTICES_LAYOUT_UNDEFINED = 0;
+    /**
+     * The vertices array uses 5 numbers to represent a point, in the format
+     * of [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
+     */
+    public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1;
+
+    @VerticesLayoutType
+    private int mVerticesLayoutType;
+
+    private TexturedMesh(Parcel in) {
+        this.mIndicesLayoutType = in.readInt();
+        this.mVerticesLayoutType = in.readInt();
+        this.mBitmap = in.readTypedObject(Bitmap.CREATOR);
+        Parcel data = Parcel.obtain();
+        try {
+            byte[] bytes = in.readBlob();
+            data.unmarshall(bytes, 0, bytes.length);
+            data.setDataPosition(0);
+            this.mIndices = data.createIntArray();
+            this.mVertices = data.createFloatArray();
+        } finally {
+            data.recycle();
+        }
+    }
+
+    private TexturedMesh(@NonNull Bitmap bitmap, @NonNull int[] indices,
+            @NonNull float[] vertices, @IndicesLayoutType int indicesLayoutType,
+            @VerticesLayoutType int verticesLayoutType) {
+        mBitmap = bitmap;
+        mIndices = indices;
+        mVertices = vertices;
+        mIndicesLayoutType = indicesLayoutType;
+        mVerticesLayoutType = verticesLayoutType;
+    }
+
+    /** Get the bitmap, which is the texture to sample from when rendering. */
+    @NonNull
+    public Bitmap getBitmap() {
+        return mBitmap;
+    }
+
+    /**
+     * Get the indices as pointers to the vertices array. Depending on the getIndicesLayoutType(),
+     * the primitives may have different shapes. For example, with INDICES_LAYOUT_TRIANGLES,
+     * indices 0, 1, 2 forms a triangle, indices 3, 4, 5 form another triangle.
+     */
+    @NonNull
+    public int[] getIndices() {
+        return mIndices;
+    }
+
+    /**
+     * Get the vertices that the index array refers to. Depending on the getVerticesLayoutType()
+     * result, the vertices array can represent different per-vertex coordinates. For example,
+     * with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of
+     * [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
+     */
+    @NonNull
+    public float[] getVertices() {
+        return mVertices;
+    }
+
+    /** Get the indices layout type. */
+    @IndicesLayoutType
+    @NonNull
+    public int getIndicesLayoutType() {
+        return mIndicesLayoutType;
+    }
+
+    /** Get the indices layout type. */
+    @VerticesLayoutType
+    @NonNull
+    public int getVerticesLayoutType() {
+        return mVerticesLayoutType;
+    }
+
+    @NonNull
+    public static final Creator<TexturedMesh> CREATOR = new Creator<TexturedMesh>() {
+        @Override
+        public TexturedMesh createFromParcel(Parcel in) {
+            return new TexturedMesh(in);
+        }
+
+        @Override
+        public TexturedMesh[] newArray(int size) {
+            return new TexturedMesh[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mIndicesLayoutType);
+        out.writeInt(mVerticesLayoutType);
+        out.writeTypedObject(mBitmap, flags);
+
+        // Indices and vertices can reach 5MB. Write the data as a Blob,
+        // which will be written to ashmem if too large.
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeIntArray(mIndices);
+            data.writeFloatArray(mVertices);
+            out.writeBlob(data.marshall());
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * A builder for {@link TexturedMesh}
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private Bitmap mBitmap;
+        private int[] mIndices;
+        private float[] mVertices;
+        @IndicesLayoutType
+        private int mIndicesLayoutType;
+        @VerticesLayoutType
+        private int mVerticesLayouttype;
+
+        /**
+         * Constructor with bitmap.
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder(@NonNull Bitmap bitmap) {
+            mBitmap = bitmap;
+        }
+
+        /**
+         * Set the required indices. The indices should represent the primitives. For example,
+         * with INDICES_LAYOUT_TRIANGLES, indices 0, 1, 2 forms a triangle, indices 3, 4, 5
+         * form another triangle.
+         */
+        @NonNull
+        public Builder setIndices(@NonNull int[] indices) {
+            mIndices = indices;
+            return this;
+        }
+
+        /**
+         * Set the required vertices. The vertices array should represent per-vertex coordinates.
+         * For example, with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of
+         * [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
+         *
+         */
+        @NonNull
+        public Builder setVertices(@NonNull float[] vertices) {
+            mVertices = vertices;
+            return this;
+        }
+
+        /**
+         * Set the required indices layout type.
+         */
+        @NonNull
+        public Builder setIndicesLayoutType(@IndicesLayoutType int indicesLayoutType) {
+            mIndicesLayoutType = indicesLayoutType;
+            return this;
+        }
+
+        /**
+         * Set the required vertices layout type.
+         */
+        @NonNull
+        public Builder setVerticesLayoutType(@VerticesLayoutType int verticesLayoutype) {
+            mVerticesLayouttype = verticesLayoutype;
+            return this;
+        }
+
+        /** Builds a TexturedMesh based on the given parameters. */
+        @NonNull
+        public TexturedMesh build() {
+            return new TexturedMesh(mBitmap, mIndices, mVertices, mIndicesLayoutType,
+                    mVerticesLayouttype);
+        }
+    }
+}
diff --git a/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java b/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java
new file mode 100644
index 0000000..5a1d27d
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wallpapereffectsgeneration;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link WallpaperEffectsGenerationManager} is the class that passes wallpaper effects
+ * generation requests to wallpaper effect generation service. For example, create a cinematic
+ * and render a cinematic live wallpaper with the response.
+ *
+ * Usage:
+ * <pre>{@code
+ *      mWallpaperEffectsGenerationManager =
+ *          context.getSystemService(WallpaperEffectsGenerationManager.class);
+ *      mWallpaperEffectsGenerationManager.
+ *          generateCinematicEffect(cinematicEffectRequest, response->{
+ *              // proceed cinematic effect response.
+ *          });
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE)
+public final class WallpaperEffectsGenerationManager {
+    /**
+     * Interface for the cinematic effect listener.
+     */
+    public interface CinematicEffectListener {
+        /**
+         * Async call when the cinematic effect response is generated.
+         * Client needs to check the status code of {@link CinematicEffectResponse}
+         * to determine if the effect generation is successful.
+         *
+         * @param response The generated cinematic effect response.
+         */
+        void onCinematicEffectGenerated(@NonNull CinematicEffectResponse response);
+    }
+
+    private final IWallpaperEffectsGenerationManager mService;
+
+    /** @hide */
+    public WallpaperEffectsGenerationManager(
+            @NonNull IWallpaperEffectsGenerationManager service) {
+        mService = service;
+    }
+
+    /**
+     * Execute a {@link android.app.wallpapereffectsgeneration.CinematicEffectRequest} from
+     * the given parameters to the wallpaper effects generation service. After the cinematic
+     * effect response is ready, the given listener is invoked by the system with the response.
+     * The listener may never receive a callback if unexpected error happened when proceeding
+     * request.
+     *
+     * @param request  request to generate cinematic effect.
+     * @param executor where the listener is invoked.
+     * @param listener listener invoked when the cinematic effect response is available.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION)
+    public void generateCinematicEffect(@NonNull CinematicEffectRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CinematicEffectListener listener) {
+        try {
+            mService.generateCinematicEffect(request,
+                    new CinematicEffectListenerWrapper(listener, executor));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static final class CinematicEffectListenerWrapper
+            extends ICinematicEffectListener.Stub {
+        @NonNull
+        private final CinematicEffectListener mListener;
+        @NonNull
+        private final Executor mExecutor;
+
+        CinematicEffectListenerWrapper(@NonNull CinematicEffectListener listener,
+                @NonNull Executor executor) {
+            mListener = listener;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onCinematicEffectGenerated(CinematicEffectResponse response) {
+            mExecutor.execute(() -> mListener.onCinematicEffectGenerated(response));
+        }
+    }
+}
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
index 941e9e2e..4e00da1 100644
--- a/core/java/android/attention/AttentionManagerInternal.java
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -46,6 +46,25 @@
      */
     public abstract void cancelAttentionCheck(AttentionCallbackInternal callback);
 
+    /**
+     * Requests the continuous updates of proximity signal via the provided callback,
+     * until the given callback is unregistered. Currently, AttentionManagerService only
+     * anticipates one client and updates one client at a time. If a new client wants to
+     * onboard to receiving Proximity updates, please make a feature request to make proximity
+     * feature multi-client before depending on this feature.
+     *
+     * @param callback      a callback that receives the proximity updates
+     * @return {@code true} if the registration should succeed.
+     */
+    public abstract boolean onStartProximityUpdates(ProximityCallbackInternal callback);
+
+    /**
+     * Requests to stop providing continuous updates until the callback is registered.
+     *
+     * @param callback a callback that was used in {@link #onStartProximityUpdates}
+     */
+    public abstract void onStopProximityUpdates(ProximityCallbackInternal callback);
+
     /** Internal interface for attention callback. */
     public abstract static class AttentionCallbackInternal {
         /**
@@ -64,4 +83,13 @@
          */
         public abstract void onFailure(int error);
     }
+
+    /** Internal interface for proximity callback. */
+    public abstract static class ProximityCallbackInternal {
+        /**
+         * @param distance the estimated distance of the user (in meter)
+         * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
+         */
+        public abstract void onProximityUpdate(double distance);
+    }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a0864d6..52681630 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5031,6 +5031,20 @@
     public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
 
     /**
+     * Used for getting the wallpaper effects generation service.
+     *
+     * <p><b>NOTE: </b> this service is optional; callers of
+     * {@code Context.getSystemServiceName(WALLPAPER_EFFECTS_GENERATION_SERVICE)} should check for
+     * {@code null}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE =
+            "wallpaper_effects_generation";
+
+    /**
      * Used to access {@link MusicRecognitionManagerService}.
      *
      * @hide
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1c82b38..30aed8b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -653,6 +653,7 @@
 
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     String getPermissionControllerPackageName();
+    String getSupplementalProcessPackageName();
 
     ParceledListSlice getInstantApps(int userId);
     byte[] getInstantAppCookie(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e5c31d7..aa64700 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5691,6 +5691,20 @@
     }
 
     /**
+     * Returns the package name of the component implementing supplemental process service.
+     *
+     * @return the package name of the component implementing supplemental process service
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public String getSupplementalProcessPackageName() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Add a new dynamic permission to the system.  For this to work, your
      * package must have defined a permission tree through the
      * {@link android.R.styleable#AndroidManifestPermissionTree
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index c12e819..d6d3a97 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -25,6 +25,7 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.graphics.Point;
 import android.hardware.CameraStatus;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
@@ -458,12 +459,14 @@
                     (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
             Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
             if (display != null) {
-                int width = display.getWidth();
-                int height = display.getHeight();
+                Point sz = new Point();
+                display.getRealSize(sz);
+                int width = sz.x;
+                int height = sz.y;
 
                 if (height > width) {
                     height = width;
-                    width = display.getHeight();
+                    width = sz.y;
                 }
 
                 ret = new Size(width, height);
@@ -471,7 +474,7 @@
                 Log.e(TAG, "Invalid default display!");
             }
         } catch (Exception e) {
-            Log.e(TAG, "getDisplaySize Failed. " + e.toString());
+            Log.e(TAG, "getDisplaySize Failed. " + e);
         }
 
         return ret;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1a7a63ae..af8ec27 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -916,6 +916,17 @@
     }
 
     /**
+     * Returns the system preferred display mode.
+     */
+    public Display.Mode getSystemPreferredDisplayMode(int displayId) {
+        try {
+            return mDm.getSystemPreferredDisplayMode(displayId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * When enabled the app requested display resolution and refresh rate is always selected
      * in DisplayModeDirector regardless of user settings and policies for low brightness, low
      * battery etc.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 35663af..b3af52b 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -168,6 +168,7 @@
     // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission.
     void setUserPreferredDisplayMode(int displayId, in Mode mode);
     Mode getUserPreferredDisplayMode(int displayId);
+    Mode getSystemPreferredDisplayMode(int displayId);
 
     // When enabled the app requested display resolution and refresh rate is always selected
     // in DisplayModeDirector regardless of user settings and policies for low brightness, low
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index e7d76f6..bb34646 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -142,7 +142,7 @@
      * @hide
      */
     public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
-        // TODO(amyjojo): implement this when needed.
+        // TODO(b/217509829): implement this when needed.
     }
 
     /**
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index cee6a5b..b96e4f8 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -21,6 +21,8 @@
 public abstract class HdmiClient {
     private static final String TAG = "HdmiClient";
 
+    private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+
     /* package */ final IHdmiControlService mService;
 
     private IHdmiVendorCommandListener mIHdmiVendorCommandListener;
@@ -156,11 +158,25 @@
     }
 
     /**
-     * Sets a listener used to receive incoming vendor-specific command.
+     * Sets a listener used to receive incoming vendor-specific command. This listener will only
+     * receive {@code <Vendor Command>} but will not receive any {@code <Vendor Command with ID>}
+     * messages.
      *
      * @param listener listener object
      */
     public void setVendorCommandListener(@NonNull VendorCommandListener listener) {
+        // Set the vendor ID to INVALID_VENDOR_ID.
+        setVendorCommandListener(listener, UNKNOWN_VENDOR_ID);
+    }
+
+    /**
+     * Sets a listener used to receive incoming vendor-specific command.
+     *
+     * @param listener listener object
+     * @param vendorId The listener is interested in {@code <Vendor Command with ID>} received with
+     *     this vendorId and all {@code <Vendor Command>} messages.
+     */
+    public void setVendorCommandListener(@NonNull VendorCommandListener listener, int vendorId) {
         if (listener == null) {
             throw new IllegalArgumentException("listener cannot be null");
         }
@@ -169,7 +185,7 @@
         }
         try {
             IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener);
-            mService.addVendorCommandListener(wrappedListener, getDeviceType());
+            mService.addVendorCommandListener(wrappedListener, vendorId);
             mIHdmiVendorCommandListener = wrappedListener;
         } catch (RemoteException e) {
             Log.e(TAG, "failed to set vendor command listener: ", e);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ec55e12..9235ba1 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -564,6 +564,32 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TvSendStandbyOnSleep {}
 
+    // -- Whether a playback device should act on an incoming {@code <Set Menu Language>} message.
+    /**
+     * Confirmation dialog should be shown upon receiving the CEC message.
+     *
+     * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE
+     * @hide
+     */
+    public static final int SET_MENU_LANGUAGE_ENABLED = 1;
+    /**
+     * The message should be ignored.
+     *
+     * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE
+     * @hide
+     */
+    public static final int SET_MENU_LANGUAGE_DISABLED = 0;
+    /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE
+     * @hide
+     */
+    @IntDef(prefix = { "SET_MENU_LANGUAGE_" }, value = {
+            SET_MENU_LANGUAGE_ENABLED,
+            SET_MENU_LANGUAGE_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SetMenuLanguage {}
+
     // -- The RC profile of a TV panel.
     /**
      * RC profile none.
@@ -818,6 +844,13 @@
     public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP =
             "tv_send_standby_on_sleep";
     /**
+     * Name of a setting deciding whether {@code <Set Menu Language>} message should be
+     * handled by the framework or ignored.
+     *
+     * @hide
+     */
+    public static final String CEC_SETTING_NAME_SET_MENU_LANGUAGE = "set_menu_language";
+    /**
      * Name of a setting representing the RC profile of a TV panel.
      *
      * @hide
@@ -983,6 +1016,7 @@
         CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
         CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
         CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+        CEC_SETTING_NAME_SET_MENU_LANGUAGE,
         CEC_SETTING_NAME_RC_PROFILE_TV,
         CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
         CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 16adee9..818554d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -221,8 +221,8 @@
         }
 
         @Override
-        public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
-            HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType);
+        public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+            HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId);
         }
 
         @Override
@@ -481,7 +481,7 @@
             boolean hasVendorId) {}
 
     /** @hide */
-    public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {}
+    public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {}
 
     /** @hide */
     public void sendStandby(int deviceType, int deviceId) {}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 6613397..35dd9ed 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -76,7 +76,7 @@
     void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
     void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
             boolean hasVendorId);
-    void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
+    void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId);
     void sendStandby(int deviceType, int deviceId);
     void setHdmiRecordListener(IHdmiRecordListener callback);
     void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 27403ec..e1ffd4a 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -112,7 +112,7 @@
     oneway void requestPointerCapture(IBinder inputChannelToken, boolean enabled);
 
     /** Create an input monitor for gestures. */
-    InputMonitor monitorGestureInput(String name, int displayId);
+    InputMonitor monitorGestureInput(IBinder token, String name, int displayId);
 
     // Add a runtime association between the input port and the display port. This overrides any
     // static associations.
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 979e9dd..2fd79cf 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -35,6 +35,7 @@
 import android.hardware.lights.LightState;
 import android.hardware.lights.LightsManager;
 import android.hardware.lights.LightsRequest;
+import android.os.Binder;
 import android.os.BlockUntrustedTouchesMode;
 import android.os.Build;
 import android.os.CombinedVibration;
@@ -1211,7 +1212,7 @@
      */
     public InputMonitor monitorGestureInput(String name, int displayId) {
         try {
-            return mIm.monitorGestureInput(name, displayId);
+            return mIm.monitorGestureInput(new Binder(), name, displayId);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1b7c00c..6330661 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -526,12 +526,15 @@
     public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
         Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
 
-        if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
+        boolean warnOnBlocking = mWarnOnBlocking; // Cache it to reduce volatile access.
+
+        if (warnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
                 && Binder.sWarnOnBlockingOnCurrentThread.get()) {
 
             // For now, avoid spamming the log by disabling after we've logged
             // about this interface at least once
             mWarnOnBlocking = false;
+            warnOnBlocking = false;
 
             if (Build.IS_USERDEBUG) {
                 // Log this as a WTF on userdebug builds.
@@ -578,7 +581,13 @@
         }
 
         try {
-            return transactNative(code, data, reply, flags);
+            final boolean result = transactNative(code, data, reply, flags);
+
+            if (reply != null && !warnOnBlocking) {
+                reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);
+            }
+
+            return result;
         } finally {
             AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 35b9ccc..9970641 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -31,6 +31,7 @@
 import android.sysprop.SocProperties;
 import android.sysprop.TelephonyProperties;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.View;
 
@@ -39,6 +40,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -396,6 +398,17 @@
          */
         public static final String CODENAME = getString("ro.build.version.codename");
 
+        /**
+         * All known codenames starting from {@link VERSION_CODES.Q}.
+         *
+         * <p>This includes in development codenames as well.
+         *
+         * @hide
+         */
+        @SystemApi
+        @NonNull public static final Set<String> KNOWN_CODENAMES =
+                new ArraySet<>(new String[]{"Q", "R", "S", "Sv2", "Tiramisu"});
+
         private static final String[] ALL_CODENAMES
                 = getStringList("ro.build.version.all_codenames", ",");
 
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index aa4b83a..0c3514f 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -165,7 +165,7 @@
     private boolean isAngleEnabledByGameMode(Context context, String packageName) {
         try {
             final boolean gameModeEnabledAngle =
-                    (mGameManager != null) && mGameManager.getAngleEnabled(packageName);
+                    (mGameManager != null) && mGameManager.isAngleEnabled(packageName);
             Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
             return gameModeEnabledAngle;
         } catch (SecurityException e) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3bc3ec8..9998e12 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -18,6 +18,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -53,6 +54,8 @@
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
 import java.io.Serializable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -229,6 +232,25 @@
 
     private RuntimeException mStack;
 
+    /** @hide */
+    @TestApi
+    public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1 << 0;
+
+    /** @hide */
+    @TestApi
+    public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 1 << 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT,
+            FLAG_PROPAGATE_ALLOW_BLOCKING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ParcelFlags {}
+
+    @ParcelFlags
+    private int mFlags;
+
     /**
      * Whether or not to parcel the stack trace of an exception. This has a performance
      * impact, so should only be included in specific processes and only on debug builds.
@@ -585,6 +607,40 @@
         nativeMarkForBinder(mNativePtr, binder);
     }
 
+    /** @hide */
+    @ParcelFlags
+    @TestApi
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide */
+    public void setFlags(@ParcelFlags int flags) {
+        mFlags = flags;
+    }
+
+    /** @hide */
+    public void addFlags(@ParcelFlags int flags) {
+        mFlags |= flags;
+    }
+
+    /** @hide */
+    private boolean hasFlags(@ParcelFlags int flags) {
+        return (mFlags & flags) == flags;
+    }
+
+    /**
+     * This method is used by the AIDL compiler for system components. Not intended to be
+     * used by non-system apps.
+     */
+    // Note: Ideally this method should be @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES),
+    // but we need to make this method public due to the way the aidl compiler is compiled.
+    // We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
+    // This would only work when used on a reply parcel by a binder object that's allowed-blocking.
+    public void setPropagateAllowBlocking() {
+        addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
+    }
+
     /**
      * Returns the total amount of data contained in the parcel.
      */
@@ -2088,6 +2144,102 @@
     }
 
     /**
+     * Flatten a homogeneous multi-dimensional array with fixed-size.  This delegates to other
+     * APIs to write a one-dimensional array.  Use {@link #readFixedArray(Object)} or
+     * {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal.
+     *
+     * @param val The array to be written.
+     * @param parcelableFlags Contextual flags as per
+     *   {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *   Used only if val is an array of Parcelable objects.
+     * @param dimensions an array of int representing length of each dimension. The array should be
+     *   sized with the exact size of dimensions.
+     *
+     * @see #readFixedArray
+     * @see #createFixedArray
+     * @see #writeBooleanArray
+     * @see #writeByteArray
+     * @see #writeCharArray
+     * @see #writeIntArray
+     * @see #writeLongArray
+     * @see #writeFloatArray
+     * @see #writeDoubleArray
+     * @see #writeBinderArray
+     * @see #writeInterfaceArray
+     * @see #writeTypedArray
+     * @throws BadParcelableException If the array's component type is not supported or if its
+     *   size doesn't match with the given dimensions.
+     */
+    public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
+            @NonNull int... dimensions) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions);
+    }
+
+    private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index,
+            int[] dimensions) {
+        if (index >= dimensions.length) {
+            throw new BadParcelableException("Array has more dimensions than expected: "
+                + dimensions.length);
+        }
+
+        int length = dimensions[index];
+
+        // val should be an array of length N
+        if (val == null) {
+            throw new BadParcelableException("Non-null array shouldn't have a null array.");
+        }
+        if (!val.getClass().isArray()) {
+            throw new BadParcelableException("Not an array: " + val);
+        }
+        if (Array.getLength(val) != length) {
+            throw new BadParcelableException("bad length: expected " + length + ", but got "
+                + Array.getLength(val));
+        }
+
+        // Delegates to other writers if this is a one-dimensional array.
+        // Otherwise, write component arrays with recursive calls.
+
+        final Class<?> componentType = val.getClass().getComponentType();
+        if (!componentType.isArray() && index + 1 != dimensions.length) {
+            throw new BadParcelableException("Array has fewer dimensions than expected: "
+                + dimensions.length);
+        }
+        if (componentType == boolean.class) {
+            writeBooleanArray((boolean[]) val);
+        } else if (componentType == byte.class) {
+            writeByteArray((byte[]) val);
+        } else if (componentType == char.class) {
+            writeCharArray((char[]) val);
+        } else if (componentType == int.class) {
+            writeIntArray((int[]) val);
+        } else if (componentType == long.class) {
+            writeLongArray((long[]) val);
+        } else if (componentType == float.class) {
+            writeFloatArray((float[]) val);
+        } else if (componentType == double.class) {
+            writeDoubleArray((double[]) val);
+        } else if (componentType == IBinder.class) {
+            writeBinderArray((IBinder[]) val);
+        } else if (IInterface.class.isAssignableFrom(componentType)) {
+            writeInterfaceArray((IInterface[]) val);
+        } else if (Parcelable.class.isAssignableFrom(componentType)) {
+            writeTypedArray((Parcelable[]) val, parcelableFlags);
+        } else if (componentType.isArray()) {
+            writeInt(length);
+            for (int i = 0; i < length; i++) {
+                writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1,
+                        dimensions);
+            }
+        } else {
+            throw new BadParcelableException("unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    /**
      * Flatten a generic object in to a parcel.  The given Object value may
      * currently be one of the following types:
      *
@@ -2949,7 +3101,15 @@
      * Read an object from the parcel at the current dataPosition().
      */
     public final IBinder readStrongBinder() {
-        return nativeReadStrongBinder(mNativePtr);
+        final IBinder result = nativeReadStrongBinder(mNativePtr);
+
+        // If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
+        // from the object that returned it.
+        if (result != null && hasFlags(
+                FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT | FLAG_PROPAGATE_ALLOW_BLOCKING)) {
+            Binder.allowBlocking(result);
+        }
+        return result;
     }
 
     /**
@@ -3788,6 +3948,317 @@
     }
 
     /**
+     * Read a new multi-dimensional array from a parcel.  If you want to read Parcelable or
+     * IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or
+     * {@link #readFixedArray(Object, Function)}.
+     * @param val the destination array to hold the read values.
+     *
+     * @see #writeTypedArray
+     * @see #readBooleanArray
+     * @see #readByteArray
+     * @see #readCharArray
+     * @see #readIntArray
+     * @see #readLongArray
+     * @see #readFloatArray
+     * @see #readDoubleArray
+     * @see #readBinderArray
+     * @see #readInterfaceArray
+     * @see #readTypedArray
+     */
+    public <T> void readFixedArray(@NonNull T val) {
+        Class<?> componentType = val.getClass().getComponentType();
+        if (componentType == boolean.class) {
+            readBooleanArray((boolean[]) val);
+        } else if (componentType == byte.class) {
+            readByteArray((byte[]) val);
+        } else if (componentType == char.class) {
+            readCharArray((char[]) val);
+        } else if (componentType == int.class) {
+            readIntArray((int[]) val);
+        } else if (componentType == long.class) {
+            readLongArray((long[]) val);
+        } else if (componentType == float.class) {
+            readFloatArray((float[]) val);
+        } else if (componentType == double.class) {
+            readDoubleArray((double[]) val);
+        } else if (componentType == IBinder.class) {
+            readBinderArray((IBinder[]) val);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length != Array.getLength(val)) {
+                throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+                    + ", but got " + length);
+            }
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i));
+            }
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    /**
+     * Read a new multi-dimensional array of typed interfaces from a parcel.
+     * If you want to read Parcelable values, use
+     * {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use
+     * {@link #readFixedArray(Object)}.
+     * @param val the destination array to hold the read values.
+     */
+    public <T, S extends IInterface> void readFixedArray(@NonNull T val,
+            @NonNull Function<IBinder, S> asInterface) {
+        Class<?> componentType = val.getClass().getComponentType();
+        if (IInterface.class.isAssignableFrom(componentType)) {
+            readInterfaceArray((S[]) val, asInterface);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length != Array.getLength(val)) {
+                throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+                    + ", but got " + length);
+            }
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), asInterface);
+            }
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    /**
+     * Read a new multi-dimensional array of typed parcelables from a parcel.
+     * If you want to read IInterface values, use
+     * {@link #readFixedArray(Object, Function)}. For values of other types, use
+     * {@link #readFixedArray(Object)}.
+     * @param val the destination array to hold the read values.
+     */
+    public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
+            @NonNull Parcelable.Creator<S> c) {
+        Class<?> componentType = val.getClass().getComponentType();
+        if (Parcelable.class.isAssignableFrom(componentType)) {
+            readTypedArray((S[]) val, c);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length != Array.getLength(val)) {
+                throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+                    + ", but got " + length);
+            }
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), c);
+            }
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) {
+        if (numDimension <= 0) {
+            throw new BadParcelableException("Fixed-size array should have dimensions.");
+        }
+
+        for (int i = 0; i < numDimension; i++) {
+            if (!cls.isArray()) {
+                throw new BadParcelableException("Array has fewer dimensions than expected: "
+                    + numDimension);
+            }
+            cls = cls.getComponentType();
+        }
+        if (cls.isArray()) {
+            throw new BadParcelableException("Array has more dimensions than expected: "
+                + numDimension);
+        }
+    }
+
+    /**
+     * Read and return a new multi-dimensional array from a parcel.  Returns null if the
+     * previously written array object is null.  If you want to read Parcelable or
+     * IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or
+     * {@link #createFixedArray(Class, Function, int[])}.
+     * @param cls  the Class object for the target array type. (e.g. int[][].class)
+     * @param dimensions an array of int representing length of each dimension.
+     *
+     * @see #writeTypedArray
+     * @see #createBooleanArray
+     * @see #createByteArray
+     * @see #createCharArray
+     * @see #createIntArray
+     * @see #createLongArray
+     * @see #createFloatArray
+     * @see #createDoubleArray
+     * @see #createBinderArray
+     * @see #createInterfaceArray
+     * @see #createTypedArray
+     */
+    @Nullable
+    public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
+        // Check if type matches with dimensions
+        // If type is one-dimensional array, delegate to other creators
+        // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+        ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+        T val = null;
+        final Class<?> componentType = cls.getComponentType();
+        if (componentType == boolean.class) {
+            val = (T) createBooleanArray();
+        } else if (componentType == byte.class) {
+            val = (T) createByteArray();
+        } else if (componentType == char.class) {
+            val = (T) createCharArray();
+        } else if (componentType == int.class) {
+            val = (T) createIntArray();
+        } else if (componentType == long.class) {
+            val = (T) createLongArray();
+        } else if (componentType == float.class) {
+            val = (T) createFloatArray();
+        } else if (componentType == double.class) {
+            val = (T) createDoubleArray();
+        } else if (componentType == IBinder.class) {
+            val = (T) createBinderArray();
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length < 0) {
+                return null;
+            }
+            if (length != dimensions[0]) {
+                throw new BadParcelableException("Bad length: expected " + dimensions[0]
+                    + ", but got " + length);
+            }
+
+            // Create a multi-dimensional array with an innermost component type and dimensions
+            Class<?> innermost = componentType.getComponentType();
+            while (innermost.isArray()) {
+                innermost = innermost.getComponentType();
+            }
+            val = (T) Array.newInstance(innermost, dimensions);
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i));
+            }
+            return val;
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+
+        // Check if val is null (which is OK) or has the expected size.
+        // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+        // are created with expected dimensions.
+        if (val != null && Array.getLength(val) != dimensions[0]) {
+            throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+                + Array.getLength(val));
+        }
+        return val;
+    }
+
+    /**
+     * Read and return a new multi-dimensional array of typed interfaces from a parcel.
+     * Returns null if the previously written array object is null.  If you want to read
+     * Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}.
+     * For values of other types use {@link #createFixedArray(Class, int[])}.
+     * @param cls  the Class object for the target array type. (e.g. IFoo[][].class)
+     * @param dimensions an array of int representing length of each dimension.
+     */
+    @Nullable
+    public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
+            @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
+        // Check if type matches with dimensions
+        // If type is one-dimensional array, delegate to other creators
+        // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+        ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+        T val = null;
+        final Class<?> componentType = cls.getComponentType();
+        if (IInterface.class.isAssignableFrom(componentType)) {
+            val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n),
+                    asInterface);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length < 0) {
+                return null;
+            }
+            if (length != dimensions[0]) {
+                throw new BadParcelableException("Bad length: expected " + dimensions[0]
+                    + ", but got " + length);
+            }
+
+            // Create a multi-dimensional array with an innermost component type and dimensions
+            Class<?> innermost = componentType.getComponentType();
+            while (innermost.isArray()) {
+                innermost = innermost.getComponentType();
+            }
+            val = (T) Array.newInstance(innermost, dimensions);
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), asInterface);
+            }
+            return val;
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+
+        // Check if val is null (which is OK) or has the expected size.
+        // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+        // are created with expected dimensions.
+        if (val != null && Array.getLength(val) != dimensions[0]) {
+            throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+                + Array.getLength(val));
+        }
+        return val;
+    }
+
+    /**
+     * Read and return a new multi-dimensional array of typed parcelables from a parcel.
+     * Returns null if the previously written array object is null.  If you want to read
+     * IInterface values, use {@link #createFixedArray(Class, Function, int[])}.
+     * For values of other types use {@link #createFixedArray(Class, int[])}.
+     * @param cls  the Class object for the target array type. (e.g. Foo[][].class)
+     * @param dimensions an array of int representing length of each dimension.
+     */
+    @Nullable
+    public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
+            @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
+        // Check if type matches with dimensions
+        // If type is one-dimensional array, delegate to other creators
+        // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+        ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+        T val = null;
+        final Class<?> componentType = cls.getComponentType();
+        if (Parcelable.class.isAssignableFrom(componentType)) {
+            val = (T) createTypedArray(c);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length < 0) {
+                return null;
+            }
+            if (length != dimensions[0]) {
+                throw new BadParcelableException("Bad length: expected " + dimensions[0]
+                    + ", but got " + length);
+            }
+
+            // Create a multi-dimensional array with an innermost component type and dimensions
+            Class<?> innermost = componentType.getComponentType();
+            while (innermost.isArray()) {
+                innermost = innermost.getComponentType();
+            }
+            val = (T) Array.newInstance(innermost, dimensions);
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), c);
+            }
+            return val;
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+
+        // Check if val is null (which is OK) or has the expected size.
+        // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+        // are created with expected dimensions.
+        if (val != null && Array.getLength(val) != dimensions[0]) {
+            throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+                + Array.getLength(val));
+        }
+        return val;
+    }
+
+    /**
      * Write a heterogeneous array of Parcelable objects into the Parcel.
      * Each object in the array is written along with its class name, so
      * that the correct class can later be instantiated.  As a result, this
@@ -4588,6 +5059,7 @@
     }
 
     private void freeBuffer() {
+        mFlags = 0;
         resetSqaushingState();
         if (mOwnsNativeParcelObject) {
             nativeFreeBuffer(mNativePtr);
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 8f50860..78f1cb1 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -546,18 +546,22 @@
     @VibrationEffectSupport
     public final int areAllEffectsSupported(
             @NonNull @VibrationEffect.EffectType int... effectIds) {
-        int support = VIBRATION_EFFECT_SUPPORT_YES;
-        for (int supported : areEffectsSupported(effectIds)) {
-            if (supported == VIBRATION_EFFECT_SUPPORT_NO) {
-                return VIBRATION_EFFECT_SUPPORT_NO;
-            } else if (supported == VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
-                support = VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+        VibratorInfo info = getInfo();
+        int allSupported = VIBRATION_EFFECT_SUPPORT_YES;
+        for (int effectId : effectIds) {
+            switch (info.isEffectSupported(effectId)) {
+                case VIBRATION_EFFECT_SUPPORT_NO:
+                    return VIBRATION_EFFECT_SUPPORT_NO;
+                case VIBRATION_EFFECT_SUPPORT_YES:
+                    continue;
+                default: // VIBRATION_EFFECT_SUPPORT_UNKNOWN
+                    allSupported = VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+                    break;
             }
         }
-        return support;
+        return allSupported;
     }
 
-
     /**
      * Query whether the vibrator supports the given primitives.
      *
@@ -598,8 +602,9 @@
      */
     public final boolean areAllPrimitivesSupported(
             @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
-        for (boolean supported : arePrimitivesSupported(primitiveIds)) {
-            if (!supported) {
+        VibratorInfo info = getInfo();
+        for (int primitiveId : primitiveIds) {
+            if (!info.isPrimitiveSupported(primitiveId)) {
                 return false;
             }
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c4fe1a4..ee6f9c0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6094,9 +6094,11 @@
         }
 
         /** @hide */
-        @UnsupportedAppUsage
-        public static String getStringForUser(ContentResolver resolver, String name,
-                int userHandle) {
+        @SystemApi
+        @Nullable
+        @SuppressLint("VisiblySynchronized")
+        public static String getStringForUser(@NonNull ContentResolver resolver,
+                @NonNull String name, int userHandle) {
             if (MOVED_TO_GLOBAL.contains(name)) {
                 Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
                         + " to android.provider.Settings.Global.");
@@ -6328,8 +6330,9 @@
         }
 
         /** @hide */
-        @UnsupportedAppUsage
-        public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+        @SystemApi
+        public static int getIntForUser(@NonNull ContentResolver cr, @NonNull String name,
+                int def, int userHandle) {
             String v = getStringForUser(cr, name, userHandle);
             return parseIntSettingWithDefault(v, def);
         }
diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java
index 49ab5db..f5c59b5 100644
--- a/core/java/android/service/attention/AttentionService.java
+++ b/core/java/android/service/attention/AttentionService.java
@@ -24,11 +24,14 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
 
 
 /**
@@ -51,6 +54,7 @@
  */
 @SystemApi
 public abstract class AttentionService extends Service {
+    private static final String LOG_TAG = "AttentionService";
     /**
      * The {@link Intent} that must be declared as handled by the service. To be supported, the
      * service must also require the {@link android.Manifest.permission#BIND_ATTENTION_SERVICE}
@@ -80,6 +84,9 @@
     /** Camera permission is not granted. */
     public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6;
 
+    /** Users’ proximity is unknown (proximity sensing was inconclusive and is unsupported). */
+    public static final double PROXIMITY_UNKNOWN = -1;
+
     /**
      * Result codes for when attention check was successful.
      *
@@ -118,6 +125,20 @@
             Preconditions.checkNotNull(callback);
             AttentionService.this.onCancelAttentionCheck(new AttentionCallback(callback));
         }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onStartProximityUpdates(IProximityCallback callback) {
+            Objects.requireNonNull(callback);
+            AttentionService.this.onStartProximityUpdates(new ProximityCallback(callback));
+
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onStopProximityUpdates() {
+            AttentionService.this.onStopProximityUpdates();
+        }
     };
 
     @Nullable
@@ -143,6 +164,23 @@
      */
     public abstract void onCancelAttentionCheck(@NonNull AttentionCallback callback);
 
+    /**
+     * Requests the continuous updates of proximity signal via the provided callback,
+     * until the given callback is unregistered.
+     *
+     * @param callback the callback to return the result to
+     */
+    public void onStartProximityUpdates(@NonNull ProximityCallback callback) {
+        Slog.w(LOG_TAG, "Override this method.");
+    }
+
+    /**
+     * Requests to stop providing continuous updates until the callback is registered.
+     */
+    public void onStopProximityUpdates() {
+        Slog.w(LOG_TAG, "Override this method.");
+    }
+
     /** Callbacks for AttentionService results. */
     public static final class AttentionCallback {
         @NonNull private final IAttentionCallback mCallback;
@@ -174,4 +212,26 @@
             }
         }
     }
+
+    /** Callbacks for ProximityCallback results. */
+    public static final class ProximityCallback {
+        @NonNull private final WeakReference<IProximityCallback> mCallback;
+
+        private ProximityCallback(@NonNull IProximityCallback callback) {
+            mCallback = new WeakReference<>(callback);
+        }
+
+        /**
+         * @param distance the estimated distance of the user (in meter)
+         * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
+         *
+         */
+        public void onProximityUpdate(double distance) {
+            try {
+                mCallback.get().onProximityUpdate(distance);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/service/attention/IAttentionService.aidl b/core/java/android/service/attention/IAttentionService.aidl
index 99e7997..8bb881b 100644
--- a/core/java/android/service/attention/IAttentionService.aidl
+++ b/core/java/android/service/attention/IAttentionService.aidl
@@ -17,6 +17,7 @@
 package android.service.attention;
 
 import android.service.attention.IAttentionCallback;
+import android.service.attention.IProximityCallback;
 
 /**
  * Interface for a concrete implementation to provide to the AttentionManagerService.
@@ -26,4 +27,6 @@
 oneway interface IAttentionService {
     void checkAttention(IAttentionCallback callback);
     void cancelAttentionCheck(IAttentionCallback callback);
+    void onStartProximityUpdates(IProximityCallback callback);
+    void onStopProximityUpdates();
 }
\ No newline at end of file
diff --git a/core/java/android/service/attention/IProximityCallback.aidl b/core/java/android/service/attention/IProximityCallback.aidl
new file mode 100644
index 0000000..9ecf9bc
--- /dev/null
+++ b/core/java/android/service/attention/IProximityCallback.aidl
@@ -0,0 +1,10 @@
+package android.service.attention;
+
+/**
+ * Callback for onStartProximityUpdates request.
+ *
+ * @hide
+ */
+oneway interface IProximityCallback {
+    void onProximityUpdate(double distance);
+}
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index 9590933..e33f180 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -84,6 +84,15 @@
         }
 
         @Override
+        public void onTransientSystemBarVisibilityFromRevealGestureChanged(
+                boolean visibleDueToGesture) {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    GameSession::dispatchTransientSystemBarVisibilityFromRevealGestureChanged,
+                    GameSession.this,
+                    visibleDueToGesture));
+        }
+
+        @Override
         public void onTaskFocusChanged(boolean focused) {
             Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
                     GameSession::moveToState, GameSession.this,
@@ -109,6 +118,7 @@
     }
 
     private LifecycleState mLifecycleState = LifecycleState.INITIALIZED;
+    private boolean mAreTransientInsetsVisibleDueToGesture = false;
     private IGameSessionController mGameSessionController;
     private int mTaskId;
     private GameSessionRootView mGameSessionRootView;
@@ -138,11 +148,23 @@
     }
 
     @Hide
-    void doDestroy() {
+    private void doDestroy() {
         mSurfaceControlViewHost.release();
         moveToState(LifecycleState.DESTROYED);
     }
 
+    /** @hide */
+    @VisibleForTesting
+    @MainThread
+    public void dispatchTransientSystemBarVisibilityFromRevealGestureChanged(
+            boolean visibleDueToGesture) {
+        boolean didValueChange = mAreTransientInsetsVisibleDueToGesture != visibleDueToGesture;
+        mAreTransientInsetsVisibleDueToGesture = visibleDueToGesture;
+        if (didValueChange) {
+            onTransientSystemBarVisibilityFromRevealGestureChanged(visibleDueToGesture);
+        }
+    }
+
     /**
      * @hide
      */
@@ -252,7 +274,23 @@
      *
      * @param focused True if the game task is focused, false if the game task is unfocused.
      */
-    public void onGameTaskFocusChanged(boolean focused) {}
+    public void onGameTaskFocusChanged(boolean focused) {
+    }
+
+    /**
+     * Called when the visibility of the transient system bars changed due to the user performing
+     * the reveal gesture. The reveal gesture is defined as a swipe to reveal the transient system
+     * bars that originates from the system bars.
+     *
+     * @param visibleDueToGesture if the transient bars triggered by the reveal gesture are visible.
+     *                            This is {@code true} when the transient system bars become visible
+     *                            due to user performing the reveal gesture. This is {@code false}
+     *                            when the transient system bars are hidden or become permanently
+     *                            visible.
+     */
+    public void onTransientSystemBarVisibilityFromRevealGestureChanged(
+            boolean visibleDueToGesture) {
+    }
 
     /**
      * Sets the task overlay content to an explicit view. This view is placed directly into the game
@@ -344,12 +382,14 @@
 
         /**
          * Called when taking the screenshot failed.
+         *
          * @param statusCode Indicates the reason for failure.
          */
         void onFailure(@ScreenshotFailureStatus int statusCode);
 
         /**
          * Called when taking the screenshot succeeded.
+         *
          * @param bitmap The screenshot.
          */
         void onSuccess(@NonNull Bitmap bitmap);
diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl
index 71da630..49c36c6 100644
--- a/core/java/android/service/games/IGameSession.aidl
+++ b/core/java/android/service/games/IGameSession.aidl
@@ -21,5 +21,6 @@
  */
 oneway interface IGameSession {
     void onDestroyed();
+    void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean visibleDueToGesture);
     void onTaskFocusChanged(boolean focused);
 }
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index 220e498..6b11e74 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -26,6 +26,7 @@
 oneway interface ITrustAgentServiceCallback {
     void grantTrust(CharSequence message, long durationMs, int flags);
     void revokeTrust();
+    void lockUser();
     void setManagingTrust(boolean managingTrust);
     void onConfigureCompleted(boolean result, IBinder token);
     void addEscrowToken(in byte[] token, int userId);
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index fba61cf..8f6e1e0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -119,16 +119,15 @@
      * automatically remove trust after some conditions are met (detailed below) with the option for
      * the agent to renew the trust again later.
      *
-     * <p>After this is called, the agent will grant trust until the platform thinks an active user
-     * is no longer using that trust. For example, if the user dismisses keyguard, the platform will
-     * remove trust (this does not automatically lock the device).
+     * <p>After this is called, the agent will grant trust until the platform thinks an active
+     * user is no longer using that trust. This can happen for any reason as determined by the
+     * platform. For example, if the user dismisses keyguard, the platform will remove trust;
+     * since this does not automatically lock the device, this results in the device locking the
+     * next time the screen turns off.
      *
      * <p>When the platform internally removes the agent's trust in this manner, an agent can
      * re-grant it (via a call to grantTrust) without the user having to unlock the device through
      * another method (e.g. PIN). This renewable state only persists for a limited time.
-     *
-     * TODO(b/213631675): Remove @hide
-     * @hide
      */
     public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 1 << 2;
 
@@ -139,9 +138,6 @@
      * Without this flag, the message passed to {@code grantTrust} is only used for debugging
      * purposes. With the flag, it may be displayed to the user as the reason why the device is
      * unlocked.
-     *
-     * TODO(b/213911325): Remove @hide
-     * @hide
      */
     public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
 
@@ -309,9 +305,6 @@
      * {@link #grantTrust(CharSequence, long, int)}.
      *
      * @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
-     *
-     * TODO(b/213631672): Add CTS tests
-     * @hide
      */
     public void onUserRequestedUnlock() {
     }
@@ -624,11 +617,15 @@
      *
      * If the user has no auth method specified, then keyguard will still be shown but can be
      * dismissed normally.
-     *
-     * TODO(b/213631675): Implement & make public
-     * @hide
      */
     public final void lockUser() {
+        if (mCallback != null) {
+            try {
+                mCallback.lockUser();
+            } catch (RemoteException e) {
+                onError("calling lockUser");
+            }
+        }
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl
similarity index 64%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
copy to core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl
index 861a4ed..ca75d2e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ b/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2022 The Android Open Source Project
  *
@@ -14,6 +15,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.mediattt;
+package android.service.wallpapereffectsgeneration;
 
-parcelable DeviceInfo;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+/**
+ * Interface from the system to WallpaperEffectsGeneration service.
+ *
+ * @hide
+ */
+oneway interface IWallpaperEffectsGenerationService {
+  void onGenerateCinematicEffect(in CinematicEffectRequest request);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java b/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java
new file mode 100644
index 0000000..18b654e
--- /dev/null
+++ b/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpapereffectsgeneration;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * A service for handling wallpaper effects generation tasks. It must implement
+ * (onGenerateCinematicEffect} method to generate response and call returnCinematicEffectResponse
+ * to send the response.
+ *
+ * <p>To extend this service, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION} permission and includes
+ * an intent filter with the {@link #SERVICE_INTERFACE} action. For example: </p>
+ * <pre>
+ *     <application>
+ *         <service android:name=".CtsWallpaperEffectsGenerationService"
+ *             android:exported="true"
+ *             android:label="CtsWallpaperEffectsGenerationService"
+ *             android:permission="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE">
+ *             <intent-filter>
+ *                 <action android:name="android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService"
+ />
+ *             </intent-filter>
+ *         </service>
+ *         <uses-library android:name="android.test.runner"/>
+ *     </application>
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class WallpaperEffectsGenerationService extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     *
+     * <p>The service must also require the
+     * {@link android.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION}
+     * permission.
+     *
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService";
+    private static final boolean DEBUG = false;
+    private static final String TAG = "WallpaperEffectsGenerationService";
+    private Handler mHandler;
+    private IWallpaperEffectsGenerationManager mService;
+
+    private final IWallpaperEffectsGenerationService  mInterface =
+            new IWallpaperEffectsGenerationService.Stub() {
+                @Override
+                public void onGenerateCinematicEffect(CinematicEffectRequest request) {
+                    mHandler.sendMessage(
+                            obtainMessage(
+                                    WallpaperEffectsGenerationService::onGenerateCinematicEffect,
+                                    WallpaperEffectsGenerationService.this, request));
+                }
+            };
+
+    /**
+     * Called when the OS receives a request for generating cinematic effect. On receiving the
+     * request, it extract cinematic information from the input and call
+     * {@link #returnCinematicEffectResponse} with the textured mesh
+     * and metadata wrapped in CinematicEffectResponse.
+     *
+     * @param request the cinematic effect request passed from the client.
+     */
+    public abstract void onGenerateCinematicEffect(@NonNull CinematicEffectRequest request);
+
+    /**
+     * Returns the cinematic effect response. Must be called when cinematic effect
+     * response is generated and ready to be sent back. Otherwise the response won't be
+     * returned.
+     *
+     * @param response the cinematic effect response returned from service provider.
+     */
+    public final void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) {
+        try {
+            mService.returnCinematicEffectResponse(response);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (DEBUG) {
+            Log.d(TAG, "onCreate WallpaperEffectsGenerationService");
+        }
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+        IBinder b = ServiceManager.getService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE);
+        mService = IWallpaperEffectsGenerationManager.Stub.asInterface(b);
+    }
+
+    @NonNull
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (DEBUG) {
+            Log.d(TAG, "onBind WallpaperEffectsGenerationService");
+        }
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        Slog.w(TAG,
+                "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+        return null;
+    }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index fa39380..246a8c9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1118,6 +1118,19 @@
     }
 
     /**
+     * Returns the system's preferred display mode. This mode will be used when the user has not
+     * specified a display-mode preference. This returns null if the boot display mode feature is
+     * not supported by system.
+     *
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public Display.Mode getSystemPreferredDisplayMode() {
+        return mGlobal.getSystemPreferredDisplayMode(getDisplayId());
+    }
+
+    /**
      * Returns the display's HDR capabilities.
      *
      * @see #isHdr()
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index ad1f201..8801fe0 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -79,13 +79,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -126,7 +130,7 @@
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
@@ -141,7 +145,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ InputMonitor(Parcel in) {
+    /* package-private */ InputMonitor(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -167,17 +171,21 @@
         }
 
         @Override
-        public InputMonitor createFromParcel(Parcel in) {
+        public InputMonitor createFromParcel(@NonNull Parcel in) {
             return new InputMonitor(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1571177265149L,
-            codegenVersion = "1.0.7",
+            time = 1637697281750L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
             inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
     @Deprecated
     private void __metadata() {}
 
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 904d7c8..6f5fea2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -197,6 +197,9 @@
     private static native int[] nativeGetCompositionDataspaces();
     private static native boolean nativeSetActiveColorMode(IBinder displayToken,
             int colorMode);
+    private static native boolean nativeGetBootDisplayModeSupport();
+    private static native void nativeSetBootDisplayMode(IBinder displayToken, int displayMode);
+    private static native void nativeClearBootDisplayMode(IBinder displayToken);
     private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on);
     private static native void nativeSetGameContentType(IBinder displayToken, boolean on);
     private static native void nativeSetDisplayPowerMode(
@@ -1878,6 +1881,8 @@
         public boolean autoLowLatencyModeSupported;
         public boolean gameContentTypeSupported;
 
+        public int preferredBootDisplayMode;
+
         @Override
         public String toString() {
             return "DynamicDisplayInfo{"
@@ -1887,7 +1892,8 @@
                     + ", activeColorMode=" + activeColorMode
                     + ", hdrCapabilities=" + hdrCapabilities
                     + ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported
-                    + ", gameContentTypeSupported" + gameContentTypeSupported + "}";
+                    + ", gameContentTypeSupported" + gameContentTypeSupported
+                    + ", preferredBootDisplayMode" + preferredBootDisplayMode + "}";
         }
 
         @Override
@@ -1899,7 +1905,8 @@
                 && activeDisplayModeId == that.activeDisplayModeId
                 && Arrays.equals(supportedColorModes, that.supportedColorModes)
                 && activeColorMode == that.activeColorMode
-                && Objects.equals(hdrCapabilities, that.hdrCapabilities);
+                && Objects.equals(hdrCapabilities, that.hdrCapabilities)
+                && preferredBootDisplayMode == that.preferredBootDisplayMode;
         }
 
         @Override
@@ -2266,6 +2273,36 @@
     /**
      * @hide
      */
+    public static boolean getBootDisplayModeSupport() {
+        return nativeGetBootDisplayModeSupport();
+    }
+
+    /** There is no associated getter for this method.  When this is set, the display is expected
+     * to start up in this mode next time the device reboots.
+     * @hide
+     */
+    public static void setBootDisplayMode(IBinder displayToken, int displayModeId) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+
+        nativeSetBootDisplayMode(displayToken, displayModeId);
+    }
+
+    /**
+     * @hide
+     */
+    public static void clearBootDisplayMode(IBinder displayToken) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+
+        nativeClearBootDisplayMode(displayToken);
+    }
+
+    /**
+     * @hide
+     */
     public static void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 347153c..cdb69e5 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1011,7 +1011,9 @@
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         ViewPager viewPager = findViewById(R.id.profile_pager);
-        outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
+        if (viewPager != null) {
+            outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
+        }
     }
 
     @Override
@@ -1019,7 +1021,9 @@
         super.onRestoreInstanceState(savedInstanceState);
         resetButtonBar();
         ViewPager viewPager = findViewById(R.id.profile_pager);
-        viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
+        if (viewPager != null) {
+            viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
+        }
         mMultiProfilePagerAdapter.clearInactiveProfileCache();
     }
 
@@ -1568,6 +1572,11 @@
             rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted;
         }
 
+        if (shouldUseMiniResolver()) {
+            configureMiniResolverContent();
+            return false;
+        }
+
         if (useLayoutWithDefault()) {
             mLayoutId = R.layout.resolver_list_with_default;
         } else {
@@ -1578,6 +1587,72 @@
         return postRebuildList(rebuildCompleted);
     }
 
+    private void configureMiniResolverContent() {
+        mLayoutId = R.layout.miniresolver;
+        setContentView(mLayoutId);
+
+        DisplayResolveInfo sameProfileResolveInfo =
+                mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0);
+        boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK;
+
+        DisplayResolveInfo otherProfileResolveInfo =
+                mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList.get(0);
+        ImageView icon = findViewById(R.id.icon);
+        // TODO: Set icon drawable to app icon.
+
+        ((TextView) findViewById(R.id.open_cross_profile)).setText(
+                getResources().getString(
+                        inWorkProfile ? R.string.miniresolver_open_in_personal
+                                : R.string.miniresolver_open_in_work,
+                        otherProfileResolveInfo.getDisplayLabel()));
+        ((Button) findViewById(R.id.use_same_profile_browser)).setText(
+                inWorkProfile ? R.string.miniresolver_use_work_browser
+                        : R.string.miniresolver_use_personal_browser);
+
+        findViewById(R.id.use_same_profile_browser).setOnClickListener(
+                v -> safelyStartActivity(sameProfileResolveInfo));
+
+        findViewById(R.id.button_open).setOnClickListener(v -> {
+            Intent intent = otherProfileResolveInfo.getResolvedIntent();
+            if (intent != null) {
+                prepareIntentForCrossProfileLaunch(intent);
+            }
+            safelyStartActivityInternal(otherProfileResolveInfo,
+                    mMultiProfilePagerAdapter.getInactiveListAdapter().mResolverListController
+                            .getUserHandle());
+        });
+    }
+
+    private boolean shouldUseMiniResolver() {
+        if (mMultiProfilePagerAdapter.getActiveListAdapter() == null
+                || mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
+            return false;
+        }
+        List<DisplayResolveInfo> sameProfileList =
+                mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList;
+        List<DisplayResolveInfo> otherProfileList =
+                mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList;
+
+        if (otherProfileList.size() != 1) {
+            Log.d(TAG, "Found " + otherProfileList.size() + " resolvers in the other profile");
+            return false;
+        }
+
+        if (otherProfileList.get(0).getResolveInfo().handleAllWebDataURI) {
+            Log.d(TAG, "Other profile is a web browser");
+            return false;
+        }
+
+        for (DisplayResolveInfo info : sameProfileList) {
+            if (!info.getResolveInfo().handleAllWebDataURI) {
+                Log.d(TAG, "Non-browser found in this profile");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     /**
      * Finishing procedures to be performed after the list has been rebuilt.
      * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList.
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index ece6f2f3..37c96e7 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -98,6 +98,10 @@
 
     private static final String DEFAULT_PACKAGE = "android";
 
+    // TODO (b/215515255): remove once we full migrate to shell transitions
+    private static final boolean SHELL_TRANSITIONS_ENABLED =
+            SystemProperties.getBoolean("persist.debug.shell_transit", false);
+
     private final Context mContext;
     private final String mTag;
 
@@ -252,6 +256,9 @@
                 resId = ent.array.getResourceId(animAttr, 0);
             }
         }
+        if (!SHELL_TRANSITIONS_ENABLED) {
+            resId = updateToLegacyIfNeeded(resId);
+        }
         resId = updateToTranslucentAnimIfNeeded(resId, transit);
         if (ResourceId.isValid(resId)) {
             return loadAnimationSafely(context, resId, mTag);
@@ -259,6 +266,24 @@
         return null;
     }
 
+    /**
+     * Replace animations that are not compatible with the legacy transition system with ones that
+     * are compatible with it.
+     * TODO (b/215515255): remove once we full migrate to shell transitions
+     */
+    private int updateToLegacyIfNeeded(int anim) {
+        if (anim == R.anim.activity_open_enter) {
+            return R.anim.activity_open_enter_legacy;
+        } else if (anim == R.anim.activity_open_exit) {
+            return R.anim.activity_open_exit_legacy;
+        } else if (anim == R.anim.activity_close_enter) {
+            return R.anim.activity_close_enter_legacy;
+        } else if (anim == R.anim.activity_close_exit) {
+            return R.anim.activity_close_exit_legacy;
+        }
+        return anim;
+    }
+
     /** Load animation by attribute Id from a specific AnimationStyle resource. */
     @Nullable
     public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 23ebc9f..51eb429 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,12 +24,14 @@
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.media.MediaRoute2Info;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.service.notification.StatusBarNotification;
 import android.view.InsetsVisibilities;
 
 import com.android.internal.statusbar.IAddTileResultCallback;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.view.AppearanceRegion;
 
@@ -296,4 +298,15 @@
 
     void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
     void cancelRequestAddTile(in String packageName);
+
+    /** Notifies System UI about an update to the media tap-to-transfer sender state. */
+    void updateMediaTapToTransferSenderDisplay(
+        int displayState,
+        in MediaRoute2Info routeInfo,
+        in IUndoMediaTransferCallback undoCallback);
+
+    /** Notifies System UI about an update to the media tap-to-transfer receiver state. */
+    void updateMediaTapToTransferReceiverDisplay(
+        int displayState,
+        in MediaRoute2Info routeInfo);
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index f28325e..0c45e5b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -24,6 +24,7 @@
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.media.MediaRoute2Info;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -33,6 +34,7 @@
 import com.android.internal.statusbar.IAddTileResultCallback;
 import com.android.internal.statusbar.ISessionListener;
 import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
 import com.android.internal.statusbar.RegisterStatusBarResult;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
@@ -196,4 +198,15 @@
     */
     void onSessionStarted(int sessionType, in InstanceId instanceId);
     void onSessionEnded(int sessionType, in InstanceId instanceId);
+
+    /** Notifies System UI about an update to the media tap-to-transfer sender state. */
+    void updateMediaTapToTransferSenderDisplay(
+        int displayState,
+        in MediaRoute2Info routeInfo,
+        in IUndoMediaTransferCallback undoCallback);
+
+    /** Notifies System UI about an update to the media tap-to-transfer receiver state. */
+    void updateMediaTapToTransferReceiverDisplay(
+        int displayState,
+        in MediaRoute2Info routeInfo);
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl
similarity index 68%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl
rename to core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl
index b47be87..3dd2980 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl
+++ b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.mediattt;
+package com.android.internal.statusbar;
 
 /**
- * An interface that will be invoked by System UI if the user choose to undo a transfer.
- *
- * Other services will implement this interface and System UI will invoke it.
+ * An interface that will be invoked if the user chooses to undo a transfer.
  */
-interface IUndoTransferCallback {
+interface IUndoMediaTransferCallback {
 
     /**
-     * Invoked by SystemUI when the user requests to undo the media transfer that just occurred.
+     * Invoked to notify callers that the user has chosen to undo the media transfer that just
+     * occurred.
      *
      * Implementors of this method are repsonsible for actually undoing the transfer.
      */
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 39f17e5..93864fa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -237,6 +237,10 @@
     // be delivered anonymously even to apps which target O+.
     final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
 
+    // These are the packages that are exempted from the background restriction applied
+    // by the system automatically, i.e., due to high background current drain.
+    final ArraySet<String> mBgRestrictionExemption = new ArraySet<>();
+
     // These are the package names of apps which should be automatically granted domain verification
     // for all of their domains. The only way these apps can be overridden by the user is by
     // explicitly disabling overall link handling support in app info.
@@ -389,6 +393,10 @@
         return mAllowIgnoreLocationSettings;
     }
 
+    public ArraySet<String> getBgRestrictionExemption() {
+        return mBgRestrictionExemption;
+    }
+
     public ArraySet<String> getLinkedApps() {
         return mLinkedApps;
     }
@@ -1049,6 +1057,20 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "bg-restriction-exemption": {
+                        if (allowOverrideAppRestrictions) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mBgRestrictionExemption.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     case "default-enabled-vr-app": {
                         if (allowAppConfigs) {
                             String pkgname = parser.getAttributeValue(null, "package");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a8cf253..9915913 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -104,6 +104,7 @@
     jfieldID hdrCapabilities;
     jfieldID autoLowLatencyModeSupported;
     jfieldID gameContentTypeSupported;
+    jfieldID preferredBootDisplayMode;
 } gDynamicDisplayInfoClassInfo;
 
 static struct {
@@ -1301,6 +1302,9 @@
 
     env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.gameContentTypeSupported,
                          info.gameContentTypeSupported);
+
+    env->SetIntField(object, gDynamicDisplayInfoClassInfo.preferredBootDisplayMode,
+                     info.preferredBootDisplayMode);
     return object;
 }
 
@@ -1638,6 +1642,27 @@
     }
 }
 
+static jboolean nativeGetBootDisplayModeSupport(JNIEnv* env, jclass clazz) {
+    bool isBootDisplayModeSupported = false;
+    SurfaceComposerClient::getBootDisplayModeSupport(&isBootDisplayModeSupported);
+    return static_cast<jboolean>(isBootDisplayModeSupported);
+}
+
+static void nativeSetBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject,
+                                     jint displayModId) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+    if (token == NULL) return;
+
+    SurfaceComposerClient::setBootDisplayMode(token, displayModId);
+}
+
+static void nativeClearBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+    if (token == NULL) return;
+
+    SurfaceComposerClient::clearBootDisplayMode(token);
+}
+
 static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
     if (token == NULL) return;
@@ -2046,6 +2071,12 @@
             (void*)nativeGetDisplayNativePrimaries },
     {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
             (void*)nativeSetActiveColorMode},
+     {"nativeGetBootDisplayModeSupport", "()Z",
+                (void*)nativeGetBootDisplayModeSupport },
+    {"nativeSetBootDisplayMode", "(Landroid/os/IBinder;I)V",
+            (void*)nativeSetBootDisplayMode },
+    {"nativeClearBootDisplayMode", "(Landroid/os/IBinder;)V",
+            (void*)nativeClearBootDisplayMode },
     {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V",
             (void*)nativeSetAutoLowLatencyMode },
     {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V",
@@ -2184,6 +2215,8 @@
             GetFieldIDOrDie(env, dynamicInfoClazz, "autoLowLatencyModeSupported", "Z");
     gDynamicDisplayInfoClassInfo.gameContentTypeSupported =
             GetFieldIDOrDie(env, dynamicInfoClazz, "gameContentTypeSupported", "Z");
+    gDynamicDisplayInfoClassInfo.preferredBootDisplayMode =
+            GetFieldIDOrDie(env, dynamicInfoClazz, "preferredBootDisplayMode", "I");
 
     jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
     gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 66d5e88..85504ce 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4125,6 +4125,16 @@
     <permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- Must be required by a
+         android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService,
+         to ensure that only the system can bind to it.
+         @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+         <p>Protection level: signature
+   -->
+    <permission android:name="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"
+        android:protectionLevel="signature" />
+
+
     <!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService,
          to ensure that only the system can bind to it.
          @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -5824,6 +5834,13 @@
     <permission android:name="android.permission.MANAGE_SMARTSPACE"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the wallpaper effects
+     generation service.
+        @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
+        android:protectionLevel="signature" />
+
+
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
          @hide  <p>Not for use by third-party applications.</p> -->
@@ -5923,6 +5940,10 @@
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
         android:protectionLevel="signature" />
+    <!-- @hide Permission that suppresses the notification when the clipboard is accessed.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION"
+                android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows modifying accessibility state.
          @hide -->
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 9fa7c54..0fefb51 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -19,16 +19,37 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
-    <scale
-        android:fromXScale="1.1"
-        android:toXScale="1"
-        android:fromYScale="1.1"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
+
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="0"
+        android:duration="450" />
+
+    <translate
+        android:fromXDelta="-10%"
+        android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
-        android:duration="400"/>
+        android:startOffset="0"
+        android:duration="450" />
+
+    <extend
+        android:fromExtendLeft="0"
+        android:fromExtendTop="0"
+        android:fromExtendRight="10%"
+        android:fromExtendBottom="0"
+        android:toExtendLeft="0"
+        android:toExtendTop="0"
+        android:toExtendRight="10%"
+        android:toExtendBottom="0"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_enter_legacy.xml b/core/res/res/anim/activity_close_enter_legacy.xml
new file mode 100644
index 0000000..9fa7c54
--- /dev/null
+++ b/core/res/res/anim/activity_close_enter_legacy.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+    <scale
+        android:fromXScale="1.1"
+        android:toXScale="1"
+        android:fromYScale="1.1"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="400"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index 1599ae8..f807c26 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -18,27 +18,38 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top">
+    android:shareInterpolator="false">
+
     <alpha
-        android:fromAlpha="1"
+        android:fromAlpha="1.0"
         android:toAlpha="0.0"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
         android:interpolator="@interpolator/linear"
-        android:startOffset="33"
-        android:duration="50"/>
-    <scale
-        android:fromXScale="1"
-        android:toXScale="0.9"
-        android:fromYScale="1"
-        android:toYScale="0.9"
-        android:pivotX="50%"
-        android:pivotY="50%"
+        android:startOffset="35"
+        android:duration="83" />
+
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="10%"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
-        android:duration="400"/>
+        android:startOffset="0"
+        android:duration="450" />
+
+    <extend
+        android:fromExtendLeft="10%"
+        android:fromExtendTop="0"
+        android:fromExtendRight="0"
+        android:fromExtendBottom="0"
+        android:toExtendLeft="10%"
+        android:toExtendTop="0"
+        android:toExtendRight="0"
+        android:toExtendBottom="0"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
 </set>
diff --git a/core/res/res/anim/activity_close_exit_legacy.xml b/core/res/res/anim/activity_close_exit_legacy.xml
new file mode 100644
index 0000000..1599ae8
--- /dev/null
+++ b/core/res/res/anim/activity_close_exit_legacy.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="33"
+        android:duration="50"/>
+    <scale
+        android:fromXScale="1"
+        android:toXScale="0.9"
+        android:fromYScale="1"
+        android:toYScale="0.9"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="400"/>
+</set>
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 38d3e8ed..1674dab 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -18,6 +18,7 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
+
     <alpha
         android:fromAlpha="0"
         android:toAlpha="1.0"
@@ -26,17 +27,27 @@
         android:fillAfter="true"
         android:interpolator="@interpolator/linear"
         android:startOffset="50"
-        android:duration="50"/>
-    <scale
-        android:fromXScale="0.85"
-        android:toXScale="1"
-        android:fromYScale="0.85"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
+        android:duration="83" />
+
+    <translate
+        android:fromXDelta="10%"
+        android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
-        android:duration="400"/>
+        android:duration="450" />
+
+    <extend
+        android:fromExtendLeft="10%"
+        android:fromExtendTop="0"
+        android:fromExtendRight="0"
+        android:fromExtendBottom="0"
+        android:toExtendLeft="10%"
+        android:toExtendTop="0"
+        android:toExtendRight="0"
+        android:toExtendBottom="0"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
 </set>
diff --git a/core/res/res/anim/activity_open_enter_legacy.xml b/core/res/res/anim/activity_open_enter_legacy.xml
new file mode 100644
index 0000000..38d3e8ed
--- /dev/null
+++ b/core/res/res/anim/activity_open_enter_legacy.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+    <alpha
+        android:fromAlpha="0"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="50"
+        android:duration="50"/>
+    <scale
+        android:fromXScale="0.85"
+        android:toXScale="1"
+        android:fromYScale="0.85"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="400"/>
+</set>
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 3865d21..372f2c8 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -19,27 +19,36 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
 
-    <!-- Fade out, over a black surface, which simulates a black scrim -->
     <alpha
-        android:fromAlpha="1"
-        android:toAlpha="0.4"
+        android:fromAlpha="1.0"
+        android:toAlpha="1.0"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="83"
-        android:duration="167"/>
+        android:interpolator="@interpolator/standard_accelerate"
+        android:startOffset="0"
+        android:duration="450" />
 
-    <scale
-        android:fromXScale="1"
-        android:toXScale="1.05"
-        android:fromYScale="1"
-        android:toYScale="1.05"
-        android:pivotX="50%"
-        android:pivotY="50%"
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="-10%"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
-        android:duration="400"/>
+        android:startOffset="0"
+        android:duration="450" />
+
+    <extend
+        android:fromExtendLeft="0"
+        android:fromExtendTop="0"
+        android:fromExtendRight="10%"
+        android:fromExtendBottom="0"
+        android:toExtendLeft="0"
+        android:toExtendTop="0"
+        android:toExtendRight="10%"
+        android:toExtendBottom="0"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_open_exit_legacy.xml b/core/res/res/anim/activity_open_exit_legacy.xml
new file mode 100644
index 0000000..3865d21
--- /dev/null
+++ b/core/res/res/anim/activity_open_exit_legacy.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+
+    <!-- Fade out, over a black surface, which simulates a black scrim -->
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="0.4"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="83"
+        android:duration="167"/>
+
+    <scale
+        android:fromXScale="1"
+        android:toXScale="1.05"
+        android:fromYScale="1"
+        android:toYScale="1.05"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="400"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml
new file mode 100644
index 0000000..44ed6f2
--- /dev/null
+++ b/core/res/res/layout/miniresolver.xml
@@ -0,0 +1,111 @@
+<?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.internal.widget.ResolverDrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:maxWidth="@dimen/resolver_max_width"
+    android:maxCollapsedHeight="@dimen/resolver_max_collapsed_height"
+    android:maxCollapsedHeightSmall="56dp"
+    android:id="@id/contentPanel">
+
+    <RelativeLayout
+        android:id="@+id/title_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alwaysShow="true"
+        android:elevation="@dimen/resolver_elevation"
+        android:paddingTop="@dimen/resolver_small_margin"
+        android:paddingStart="@dimen/resolver_edge_margin"
+        android:paddingEnd="@dimen/resolver_edge_margin"
+        android:paddingBottom="@dimen/resolver_title_padding_bottom"
+        android:background="@drawable/bottomsheet_background">
+
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_centerHorizontal="true"
+        />
+
+        <TextView
+            android:id="@+id/open_cross_profile"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/icon"
+            android:layout_centerHorizontal="true"
+            android:textColor="?android:textColorPrimary"
+        />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/button_bar_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alwaysShow="true"
+        android:orientation="vertical"
+        android:background="?attr/colorBackground"
+        android:layout_ignoreOffset="true">
+        <View
+            android:id="@+id/resolver_button_bar_divider"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="?attr/colorBackground"
+            android:foreground="?attr/dividerVertical" />
+        <RelativeLayout
+            style="?attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_ignoreOffset="true"
+            android:layout_hasNestedScrollIndicator="true"
+            android:gravity="end|center_vertical"
+            android:orientation="horizontal"
+            android:layoutDirection="locale"
+            android:measureWithLargestChild="true"
+            android:paddingTop="@dimen/resolver_button_bar_spacing"
+            android:paddingBottom="@dimen/resolver_button_bar_spacing"
+            android:paddingStart="@dimen/resolver_edge_margin"
+            android:paddingEnd="@dimen/resolver_small_margin"
+            android:elevation="@dimen/resolver_elevation">
+
+            <Button
+                android:id="@+id/use_same_profile_browser"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentStart="true"
+                android:maxLines="2"
+                style="@android:style/Widget.DeviceDefault.Button.Borderless"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+                android:textAllCaps="false"
+                android:text="@string/activity_resolver_use_once"
+            />
+
+            <Button
+                android:id="@+id/button_open"
+                android:layout_width="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:maxLines="2"
+                style="@android:style/Widget.DeviceDefault.Button.Colored"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+                android:textAllCaps="false"
+                android:layout_height="wrap_content"
+                android:text="@string/whichViewApplicationLabel"
+            />
+        </RelativeLayout>
+    </LinearLayout>
+</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 04e2989..afe0f1b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8875,6 +8875,22 @@
         <attr name="gameSessionService" format="string" />
     </declare-styleable>
 
+    <!-- Use <code>game-mode-config</code> as the root tag of the XML resource that
+         describes a GameModeConfig.
+         Described here are the attributes that can be included in that tag. -->
+    <declare-styleable name="GameModeConfig">
+        <!-- Set true to opt in BATTERY mode. -->
+        <attr name="supportsBatteryGameMode" format="boolean" />
+        <!-- Set true to opt in PERFORMANCE mode. -->
+        <attr name="supportsPerformanceGameMode" format="boolean" />
+        <!-- Set true to enable ANGLE. -->
+        <attr name="allowGameAngleDriver" format="boolean" />
+        <!-- Set true to allow resolution downscaling intervention. -->
+        <attr name="allowGameDownscaling" format="boolean" />
+        <!-- Set true to allow FPS override intervention. -->
+        <attr name="allowGameFpsOverride" format="boolean" />
+    </declare-styleable>
+
     <!-- Use <code>voice-enrollment-application</code>
          as the root tag of the XML resource that escribes the supported keyphrases (hotwords)
          by the enrollment application.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a06b2cb..53cf463 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2376,6 +2376,12 @@
     <!-- ComponentNames of the dreams that we should hide -->
     <string-array name="config_disabledDreamComponents" translatable="false">
     </string-array>
+    <!-- The list of supported dream complications -->
+    <integer-array name="config_supportedDreamComplications">
+    </integer-array>
+    <!-- The list of dream complications which should be enabled by default -->
+    <integer-array name="config_dreamComplicationsEnabledByDefault">
+    </integer-array>
 
     <!-- Are we allowed to dream while not plugged in? -->
     <bool name="config_dreamsEnabledOnBattery">false</bool>
@@ -4173,6 +4179,15 @@
 
     <string name="config_defaultMusicRecognitionService" translatable="false"></string>
 
+    <!-- The package name for the system's wallpaper effects generation service.
+    This service returns wallpaper effects results.
+    This service must be trusted, as it can be activated without explicit consent of the user.
+    If no service with the specified name exists on the device, wallpaper effects
+    generation service will be disabled.
+    Example: "com.android.intelligence/.WallpaperEffectsGenerationService"
+-->
+    <string name="config_defaultWallpaperEffectsGenerationService" translatable="false"></string>
+
     <!-- The package name for the default retail demo app.
          This package must be trusted, as it has the permissions to query the usage stats on the
          device.
@@ -5267,6 +5282,12 @@
     <bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool>
     <bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool>
 
+    <bool name="config_cecSetMenuLanguage_userConfigurable">true</bool>
+    <bool name="config_cecSetMenuLanguageEnabled_allowed">true</bool>
+    <bool name="config_cecSetMenuLanguageEnabled_default">true</bool>
+    <bool name="config_cecSetMenuLanguageDisabled_allowed">true</bool>
+    <bool name="config_cecSetMenuLanguageDisabled_default">false</bool>
+
     <bool name="config_cecRcProfileTv_userConfigurable">true</bool>
     <bool name="config_cecRcProfileTvNone_allowed">true</bool>
     <bool name="config_cecRcProfileTvNone_default">true</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 047c04b..d57f5ba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3255,6 +3255,11 @@
     <public name="showClockAndComplications" />
     <!-- @hide @SystemApi -->
     <public name="gameSessionService" />
+    <public name="supportsBatteryGameMode" />
+    <public name="supportsPerformanceGameMode" />
+    <public name="allowGameAngleDriver" />
+    <public name="allowGameDownscaling" />
+    <public name="allowGameFpsOverride" />
     <public name="localeConfig" />
     <public name="showBackground" />
     <public name="inheritKeyStoreKeys" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 610c6a6..52c6205 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5857,6 +5857,8 @@
     <string name="accessibility_system_action_lock_screen_label">Lock Screen</string>
     <!-- Label for taking screenshot action [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_screenshot_label">Screenshot</string>
+    <!-- Label for headset hook action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_headset_hook_label">Headset Hook</string>
     <!-- Label for triggering on-screen accessibility shortcut action [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_on_screen_a11y_shortcut_label">On-screen Accessibility Shortcut</string>
     <!-- Label for showing on-screen accessibility shortcut chooser action [CHAR LIMIT=NONE] -->
@@ -5865,6 +5867,16 @@
     <string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
     <!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
+    <!-- Label for Dpad up action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_dpad_up_label">Dpad Up</string>
+    <!-- Label for Dpad down action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_dpad_down_label">Dpad Down</string>
+    <!-- Label for Dpad left action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_dpad_left_label">Dpad Left</string>
+    <!-- Label for Dpad right action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_dpad_right_label">Dpad Right</string>
+    <!-- Label for Dpad center action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_dpad_center_label">Dpad Center</string>
     <!-- Accessibility description of caption view -->
     <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
 
@@ -5924,9 +5936,9 @@
     <string name="resolver_no_personal_apps_available">No personal apps</string>
 
     <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
-    <string name="miniresolver_open_in_personal">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in personal profile?</string>
+    <string name="miniresolver_open_in_personal">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your personal profile?</string>
     <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
-    <string name="miniresolver_open_in_work">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in work profile?</string>
+    <string name="miniresolver_open_in_work">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your work profile?</string>
     <!-- Button option. Open the link in the personal browser. [CHAR LIMIT=NONE] -->
     <string name="miniresolver_use_personal_browser">Use personal browser</string>
     <!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4c1cc4d..facfdb2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1599,6 +1599,13 @@
   <java-symbol type="layout" name="resolver_list_per_profile" />
   <java-symbol type="layout" name="chooser_list_per_profile" />
   <java-symbol type="layout" name="resolver_empty_states" />
+  <java-symbol type="id" name="open_cross_profile" />
+  <java-symbol type="string" name="miniresolver_open_in_personal" />
+  <java-symbol type="string" name="miniresolver_open_in_work" />
+  <java-symbol type="string" name="miniresolver_use_personal_browser" />
+  <java-symbol type="string" name="miniresolver_use_work_browser" />
+  <java-symbol type="id" name="button_open" />
+  <java-symbol type="id" name="use_same_profile_browser" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
@@ -1697,7 +1704,13 @@
   <java-symbol type="anim" name="activity_translucent_open_enter" />
   <java-symbol type="anim" name="activity_translucent_close_exit" />
   <java-symbol type="anim" name="activity_open_enter" />
+  <java-symbol type="anim" name="activity_open_exit" />
+  <java-symbol type="anim" name="activity_close_enter" />
   <java-symbol type="anim" name="activity_close_exit" />
+  <java-symbol type="anim" name="activity_open_enter_legacy" />
+  <java-symbol type="anim" name="activity_open_exit_legacy" />
+  <java-symbol type="anim" name="activity_close_enter_legacy" />
+  <java-symbol type="anim" name="activity_close_exit_legacy" />
   <java-symbol type="anim" name="task_fragment_close_enter" />
   <java-symbol type="anim" name="task_fragment_close_exit" />
   <java-symbol type="anim" name="task_fragment_open_enter" />
@@ -2215,6 +2228,8 @@
   <java-symbol type="integer" name="config_dreamsBatteryLevelMinimumWhenNotPowered" />
   <java-symbol type="integer" name="config_dreamsBatteryLevelDrainCutoff" />
   <java-symbol type="string" name="config_dreamsDefaultComponent" />
+  <java-symbol type="array" name="config_supportedDreamComplications" />
+  <java-symbol type="array" name="config_dreamComplicationsEnabledByDefault" />
   <java-symbol type="drawable" name="default_dream_preview" />
   <java-symbol type="array" name="config_disabledDreamComponents" />
   <java-symbol type="string" name="config_dozeComponent" />
@@ -2707,6 +2722,7 @@
   <java-symbol type="bool" name="config_allow_ussd_over_ims" />
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
+  <java-symbol type="layout" name="miniresolver" />
   <java-symbol type="string" name="activity_resolver_use_always" />
   <java-symbol type="string" name="whichApplicationNamed" />
   <java-symbol type="string" name="whichApplicationLabel" />
@@ -3673,6 +3689,7 @@
   <java-symbol type="string" name="config_defaultContentSuggestionsService" />
   <java-symbol type="string" name="config_defaultSearchUiService" />
   <java-symbol type="string" name="config_defaultSmartspaceService" />
+  <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" />
   <java-symbol type="string" name="config_defaultMusicRecognitionService" />
   <java-symbol type="string" name="config_defaultAttentionService" />
   <java-symbol type="string" name="config_defaultRotationResolverService" />
@@ -4129,10 +4146,16 @@
   <java-symbol type="string" name="accessibility_system_action_quick_settings_label" />
   <java-symbol type="string" name="accessibility_system_action_recents_label" />
   <java-symbol type="string" name="accessibility_system_action_screenshot_label" />
+  <java-symbol type="string" name="accessibility_system_action_headset_hook_label" />
   <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_label" />
   <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
   <java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
   <java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
+  <java-symbol type="string" name="accessibility_system_action_dpad_up_label" />
+  <java-symbol type="string" name="accessibility_system_action_dpad_down_label" />
+  <java-symbol type="string" name="accessibility_system_action_dpad_left_label" />
+  <java-symbol type="string" name="accessibility_system_action_dpad_right_label" />
+  <java-symbol type="string" name="accessibility_system_action_dpad_center_label" />
 
   <java-symbol type="string" name="accessibility_freeform_caption" />
 
@@ -4453,6 +4476,12 @@
   <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" />
   <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" />
 
+  <java-symbol type="bool" name="config_cecSetMenuLanguage_userConfigurable" />
+  <java-symbol type="bool" name="config_cecSetMenuLanguageEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecSetMenuLanguageEnabled_default" />
+  <java-symbol type="bool" name="config_cecSetMenuLanguageDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecSetMenuLanguageDisabled_default" />
+
   <java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" />
   <java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" />
   <java-symbol type="bool" name="config_cecRcProfileTvNone_default" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 75d2025..2817728f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -731,6 +731,25 @@
     }
 
     @Test
+    public void testMiniResolver() {
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTest(1);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(1);
+        // Personal profile only has a browser
+        personalResolvedComponentInfos.get(0).getResolveInfoAt(0).handleAllWebDataURI = true;
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendImageIntent();
+        sendIntent.setType("TestType");
+
+        mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed()));
+    }
+
+    @Test
     public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index f04a9f7..e16a2f8 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -288,9 +288,8 @@
         }
 
         @Override
-        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
-                final int deviceType) {
-        }
+        public void addVendorCommandListener(
+                final IHdmiVendorCommandListener listener, final int vendorId) {}
 
         @Override
         public void sendVendorCommand(final int deviceType, final int targetAddress,
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index ddcab6e..5dcc599 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -61,5 +61,6 @@
         <permission name="android.permission.READ_DREAM_SUPPRESSION"/>
         <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
+        <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b3dcc34..d002601 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -581,6 +581,7 @@
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
+        <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.bips">
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index cdf746f..f440b69 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -454,7 +454,8 @@
      * @param challenge is a non-empty byte array whose contents should be unique, fresh and
      *                  provided by the issuing authority. The value provided is embedded in the
      *                  generated CBOR and enables the issuing authority to verify that the
-     *                  returned proof is fresh.
+     *                  returned proof is fresh. Implementations are required to support
+     *                  challenges at least 32 bytes of length.
      * @return the COSE_Sign1 data structure above
      */
     public @NonNull byte[] proveOwnership(@NonNull byte[] challenge)  {
@@ -485,7 +486,8 @@
      * @param challenge is a non-empty byte array whose contents should be unique, fresh and
      *                  provided by the issuing authority. The value provided is embedded in the
      *                  generated CBOR and enables the issuing authority to verify that the
-     *                  returned proof is fresh.
+     *                  returned proof is fresh. Implementations are required to support
+     *                  challenges at least 32 bytes of length.
      * @return the COSE_Sign1 data structure above
      */
     public @NonNull byte[] delete(@NonNull byte[] challenge)  {
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
index 305d0ea..6d56964 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -59,7 +59,8 @@
      * @param challenge is a non-empty byte array whose contents should be unique, fresh and
      *                  provided by the issuing authority. The value provided is embedded in the
      *                  attestation extension and enables the issuing authority to verify that the
-     *                  attestation certificate is fresh.
+     *                  attestation certificate is fresh. Implementations are required to support
+     *                  challenges at least 32 bytes of length.
      * @return the X.509 certificate for this credential's CredentialKey.
      */
     public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain(
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 17005ea..6b0d7f5 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
@@ -867,6 +867,10 @@
     }
 
     private void fadeExistingPip(boolean show) {
+        if (mLeash == null || !mLeash.isValid()) {
+            Log.w(TAG, "Invalid leash on fadeExistingPip: " + mLeash);
+            return;
+        }
         final float alphaStart = show ? 0 : 1;
         final float alphaEnd = show ? 1 : 0;
         mPipAnimationController
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index 86dc6e0..88b9777 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -27,6 +27,7 @@
  */
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class BtProfileConnectionInfo implements Parcelable {
+
     private final int mProfile;
     private final boolean mSupprNoisy;
     private final int mVolume;
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index 0f5bf08..14a9144 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -36,13 +36,8 @@
 public class DemuxCapabilities {
 
     /** @hide */
-    @IntDef(flag = true, value = {
-            Filter.TYPE_TS,
-            Filter.TYPE_MMTP,
-            Filter.TYPE_IP,
-            Filter.TYPE_TLV,
-            Filter.TYPE_ALP
-    })
+    @IntDef(value = {Filter.TYPE_TS, Filter.TYPE_MMTP, Filter.TYPE_IP, Filter.TYPE_TLV,
+                    Filter.TYPE_ALP})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FilterCapabilities {}
 
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 9f44236..14accaa 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -153,8 +153,8 @@
 
 
     /** @hide */
-    @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_DATA_READY, STATUS_LOW_WATER,
-            STATUS_HIGH_WATER, STATUS_OVERFLOW})
+    @IntDef(prefix = "STATUS_",
+            value = {STATUS_DATA_READY, STATUS_LOW_WATER, STATUS_HIGH_WATER, STATUS_OVERFLOW})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Status {}
 
@@ -185,8 +185,7 @@
     public static final int STATUS_OVERFLOW = DemuxFilterStatus.OVERFLOW;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "SCRAMBLING_STATUS_",
+    @IntDef(prefix = "SCRAMBLING_STATUS_",
             value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED,
                     SCRAMBLING_STATUS_SCRAMBLED})
     @Retention(RetentionPolicy.SOURCE)
@@ -209,8 +208,7 @@
             android.hardware.tv.tuner.ScramblingStatus.SCRAMBLED;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MONITOR_EVENT_",
+    @IntDef(prefix = "MONITOR_EVENT_",
             value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface MonitorEventMask {}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index d34581d..b16d9fb 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -40,8 +40,7 @@
      *
      * @hide
      */
-    @IntDef(flag = true,
-            value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
+    @IntDef(value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
                     TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
                     TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
                     TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR,
@@ -165,7 +164,6 @@
      * @hide
      */
     @IntDef(prefix = "SC_INDEX_",
-            flag = true,
             value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME,
                     SC_INDEX_SEQUENCE, SC_INDEX_I_SLICE, SC_INDEX_P_SLICE,
                     SC_INDEX_B_SLICE, SC_INDEX_SI_SLICE, SC_INDEX_SP_SLICE})
@@ -214,8 +212,7 @@
      *
      * @hide
      */
-    @IntDef(flag = true,
-            value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
+    @IntDef(value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
             SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP,
             SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP,
             SC_HEVC_INDEX_SLICE_TRAIL_CRA})
@@ -258,8 +255,7 @@
     /**
      * @hide
      */
-    @IntDef(flag = true,
-            prefix = "SC_",
+    @IntDef(prefix = "SC_",
             value = {
                 SC_INDEX_I_FRAME,
                 SC_INDEX_P_FRAME,
diff --git a/media/java/android/media/tv/tuner/filter/SharedFilter.java b/media/java/android/media/tv/tuner/filter/SharedFilter.java
index 740ab9c..21964ee 100644
--- a/media/java/android/media/tv/tuner/filter/SharedFilter.java
+++ b/media/java/android/media/tv/tuner/filter/SharedFilter.java
@@ -38,7 +38,7 @@
 @SystemApi
 public final class SharedFilter implements AutoCloseable {
     /** @hide */
-    @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_INACCESSIBLE})
+    @IntDef(prefix = "STATUS_", value = {STATUS_INACCESSIBLE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Status {}
 
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index e0405ef..6c1134a 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -36,8 +36,7 @@
 @SystemApi
 public class AnalogFrontendSettings extends FrontendSettings {
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "SIGNAL_TYPE_",
+    @IntDef(prefix = "SIGNAL_TYPE_",
             value = {SIGNAL_TYPE_UNDEFINED, SIGNAL_TYPE_AUTO, SIGNAL_TYPE_PAL, SIGNAL_TYPE_PAL_M,
               SIGNAL_TYPE_PAL_N, SIGNAL_TYPE_PAL_60, SIGNAL_TYPE_NTSC, SIGNAL_TYPE_NTSC_443,
               SIGNAL_TYPE_SECAM})
@@ -82,8 +81,7 @@
     public static final int SIGNAL_TYPE_SECAM = FrontendAnalogType.SECAM;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "SIF_",
+    @IntDef(prefix = "SIF_",
             value = {SIF_UNDEFINED, SIF_AUTO, SIF_BG, SIF_BG_A2, SIF_BG_NICAM, SIF_I, SIF_DK,
             SIF_DK1_A2, SIF_DK2_A2, SIF_DK3_A2, SIF_DK_NICAM, SIF_L, SIF_M, SIF_M_BTSC, SIF_M_A2,
             SIF_M_EIAJ, SIF_I_NICAM, SIF_L_NICAM, SIF_L_PRIME})
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index a7157e2..c99f911 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -39,8 +39,7 @@
 public class Atsc3FrontendSettings extends FrontendSettings {
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "BANDWIDTH_",
+    @IntDef(prefix = "BANDWIDTH_",
             value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_BANDWIDTH_6MHZ,
                     BANDWIDTH_BANDWIDTH_7MHZ, BANDWIDTH_BANDWIDTH_8MHZ})
     @Retention(RetentionPolicy.SOURCE)
@@ -69,8 +68,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO,
                     MODULATION_MOD_QPSK, MODULATION_MOD_16QAM,
                     MODULATION_MOD_64QAM, MODULATION_MOD_256QAM,
@@ -113,8 +111,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "TIME_INTERLEAVE_MODE_",
+    @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
             value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
                     TIME_INTERLEAVE_MODE_CTI, TIME_INTERLEAVE_MODE_HTI})
     @Retention(RetentionPolicy.SOURCE)
@@ -140,8 +137,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "CODERATE_",
+    @IntDef(prefix = "CODERATE_",
             value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_15, CODERATE_3_15, CODERATE_4_15,
                     CODERATE_5_15, CODERATE_6_15, CODERATE_7_15, CODERATE_8_15, CODERATE_9_15,
                     CODERATE_10_15, CODERATE_11_15, CODERATE_12_15, CODERATE_13_15})
@@ -207,8 +203,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "FEC_",
+    @IntDef(prefix = "FEC_",
             value = {FEC_UNDEFINED, FEC_AUTO, FEC_BCH_LDPC_16K, FEC_BCH_LDPC_64K, FEC_CRC_LDPC_16K,
                     FEC_CRC_LDPC_64K, FEC_LDPC_16K, FEC_LDPC_64K})
     @Retention(RetentionPolicy.SOURCE)
@@ -249,8 +244,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "DEMOD_OUTPUT_FORMAT_",
+    @IntDef(prefix = "DEMOD_OUTPUT_FORMAT_",
             value = {DEMOD_OUTPUT_FORMAT_UNDEFINED, DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET,
                     DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index 3071ce8..64c6ce6 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -34,8 +34,7 @@
 public class AtscFrontendSettings extends FrontendSettings {
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_8VSB,
                     MODULATION_MOD_16VSB})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
index 6b5d6ca..07c1fbf 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -43,8 +43,7 @@
 public final class DtmbFrontendSettings extends FrontendSettings {
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "BANDWIDTH_",
+    @IntDef(prefix = "BANDWIDTH_",
             value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Bandwidth {}
@@ -68,8 +67,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "TIME_INTERLEAVE_MODE_",
+    @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
             value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
                     TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720})
     @Retention(RetentionPolicy.SOURCE)
@@ -97,8 +95,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "GUARD_INTERVAL_",
+    @IntDef(prefix = "GUARD_INTERVAL_",
             value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
             GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST,
             GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST,
@@ -143,8 +140,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO,
                     MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR,
                     MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM,
@@ -187,8 +183,7 @@
             FrontendDtmbModulation.CONSTELLATION_64QAM;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "CODERATE_",
+    @IntDef(prefix = "CODERATE_",
             value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5})
     @Retention(RetentionPolicy.SOURCE)
     public @interface CodeRate {}
@@ -215,8 +210,7 @@
     public static final int CODERATE_4_5 = FrontendDtmbCodeRate.CODERATE_4_5;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "TRANSMISSION_MODE_",
+    @IntDef(prefix = "TRANSMISSION_MODE_",
             value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO,
                     TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index afe953d..45bfc09 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -40,8 +40,7 @@
 public class DvbcFrontendSettings extends FrontendSettings {
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_16QAM,
                     MODULATION_MOD_32QAM, MODULATION_MOD_64QAM, MODULATION_MOD_128QAM,
                     MODULATION_MOD_256QAM})
@@ -98,8 +97,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "ANNEX_",
+    @IntDef(prefix = "ANNEX_",
             value = {ANNEX_UNDEFINED, ANNEX_A, ANNEX_B, ANNEX_C})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Annex {}
@@ -159,8 +157,7 @@
             android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "TIME_INTERLEAVE_MODE_",
+    @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
             value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
                     TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1,
                     TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4,
@@ -226,8 +223,7 @@
             FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "BANDWIDTH_",
+    @IntDef(prefix = "BANDWIDTH_",
             value = {BANDWIDTH_UNDEFINED, BANDWIDTH_5MHZ, BANDWIDTH_6MHZ, BANDWIDTH_7MHZ,
                     BANDWIDTH_8MHZ})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index e16f192..56dbb48 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -42,8 +42,7 @@
 @SystemApi
 public class DvbsFrontendSettings extends FrontendSettings {
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "SCAN_TYPE_",
+    @IntDef(prefix = "SCAN_TYPE_",
             value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC,
                     SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS})
     @Retention(RetentionPolicy.SOURCE)
@@ -75,8 +74,7 @@
     public static final int SCAN_TYPE_JESS = FrontendDvbsScanType.JESS;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK,
                     MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK,
                     MODULATION_MOD_32PSK, MODULATION_MOD_ACM, MODULATION_MOD_8APSK,
@@ -207,8 +205,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "STANDARD_",
+    @IntDef(prefix = "STANDARD_",
             value = {STANDARD_AUTO, STANDARD_S, STANDARD_S2, STANDARD_S2X})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Standard {}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index d86e9a8..06547e1 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -42,8 +42,7 @@
 public class DvbtFrontendSettings extends FrontendSettings {
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "TRANSMISSION_MODE_",
+    @IntDef(prefix = "TRANSMISSION_MODE_",
             value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO,
                     TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K,
                     TRANSMISSION_MODE_1K, TRANSMISSION_MODE_16K, TRANSMISSION_MODE_32K})
@@ -98,8 +97,7 @@
             FrontendDvbtTransmissionMode.MODE_32K_E;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "BANDWIDTH_",
+    @IntDef(prefix = "BANDWIDTH_",
             value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ,
                     BANDWIDTH_6MHZ, BANDWIDTH_5MHZ, BANDWIDTH_1_7MHZ, BANDWIDTH_10MHZ})
     @Retention(RetentionPolicy.SOURCE)
@@ -140,8 +138,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "CONSTELLATION_",
+    @IntDef(prefix = "CONSTELLATION_",
             value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK,
                     CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM,
                     CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R,
@@ -192,8 +189,7 @@
             FrontendDvbtConstellation.CONSTELLATION_256QAM_R;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "HIERARCHY_",
+    @IntDef(prefix = "HIERARCHY_",
             value = {HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE,
             HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH,
             HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH})
@@ -243,8 +239,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "CODERATE_",
+    @IntDef(prefix = "CODERATE_",
             value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4,
             CODERATE_5_6, CODERATE_7_8, CODERATE_3_5, CODERATE_4_5, CODERATE_6_7, CODERATE_8_9})
     @Retention(RetentionPolicy.SOURCE)
@@ -296,8 +291,7 @@
     public static final int CODERATE_8_9 = FrontendDvbtCoderate.CODERATE_8_9;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "GUARD_INTERVAL_",
+    @IntDef(prefix = "GUARD_INTERVAL_",
             value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
             GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16,
             GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4,
@@ -346,8 +340,7 @@
     public static final int GUARD_INTERVAL_19_256 = FrontendDvbtGuardInterval.INTERVAL_19_256;
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "STANDARD",
+    @IntDef(prefix = "STANDARD_",
             value = {STANDARD_AUTO, STANDARD_T, STANDARD_T2}
     )
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 38bffec..2f45a70 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -89,8 +89,7 @@
 
 
     /** @hide */
-    @LongDef(flag = true,
-            prefix = "FEC_",
+    @LongDef(prefix = "FEC_",
             value = {FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
             FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
             FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20,
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 726fe15..7e83d15 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -36,8 +36,7 @@
 @SystemApi
 public class Isdbs3FrontendSettings extends FrontendSettings {
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK,
             MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16APSK,
             MODULATION_MOD_32APSK})
@@ -75,8 +74,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            prefix = "CODERATE_",
+    @IntDef(prefix = "CODERATE_",
             value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_3, CODERATE_2_5, CODERATE_1_2,
                     CODERATE_3_5, CODERATE_2_3, CODERATE_3_4, CODERATE_7_9, CODERATE_4_5,
                     CODERATE_5_6, CODERATE_7_8, CODERATE_9_10})
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 51ec5ae..5029453 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -54,8 +54,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK,
                     MODULATION_MOD_QPSK, MODULATION_MOD_TC8PSK})
     @Retention(RetentionPolicy.SOURCE)
@@ -84,8 +83,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "CODERATE_",
+    @IntDef(prefix = "CODERATE_",
             value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4,
                     CODERATE_5_6, CODERATE_7_8})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index 89512a0..f08a514 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -40,8 +40,7 @@
 @SystemApi
 public class IsdbtFrontendSettings extends FrontendSettings {
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODULATION_",
+    @IntDef(prefix = "MODULATION_",
             value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_DQPSK,
                     MODULATION_MOD_QPSK, MODULATION_MOD_16QAM, MODULATION_MOD_64QAM})
     @Retention(RetentionPolicy.SOURCE)
@@ -74,8 +73,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "MODE_",
+    @IntDef(prefix = "MODE_",
             value = {MODE_UNDEFINED, MODE_AUTO, MODE_1, MODE_2, MODE_3})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Mode {}
@@ -103,8 +101,7 @@
 
 
     /** @hide */
-    @IntDef(flag = true,
-            prefix = "BANDWIDTH_",
+    @IntDef(prefix = "BANDWIDTH_",
             value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ,
                     BANDWIDTH_6MHZ})
     @Retention(RetentionPolicy.SOURCE)
@@ -132,7 +129,7 @@
     public static final int BANDWIDTH_6MHZ = FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
 
     /** @hide */
-    @IntDef(flag = true, prefix = "PARTIAL_RECEPTION_FLAG_",
+    @IntDef(prefix = "PARTIAL_RECEPTION_FLAG_",
             value = {PARTIAL_RECEPTION_FLAG_UNDEFINED, PARTIAL_RECEPTION_FLAG_FALSE,
                     PARTIAL_RECEPTION_FLAG_TRUE})
     @Retention(RetentionPolicy.SOURCE)
@@ -153,7 +150,7 @@
     public static final int PARTIAL_RECEPTION_FLAG_TRUE = FrontendIsdbtPartialReceptionFlag.TRUE;
 
     /** @hide */
-    @IntDef(flag = true, prefix = "TIME_INTERLEAVE_MODE_",
+    @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
             value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
                     TIME_INTERLEAVE_MODE_1_0, TIME_INTERLEAVE_MODE_1_4, TIME_INTERLEAVE_MODE_1_8,
                     TIME_INTERLEAVE_MODE_1_16, TIME_INTERLEAVE_MODE_2_0, TIME_INTERLEAVE_MODE_2_2,
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f9a1774..b7beb6e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -309,6 +309,9 @@
     android_res_nquery; # introduced=29
     android_res_nresult; # introduced=29
     android_res_nsend; # introduced=29
+    android_tag_socket_with_uid; # introduced=Tiramisu
+    android_tag_socket; # introduced=Tiramisu
+    android_untag_socket; # introduced=Tiramisu
     AThermal_acquireManager; # introduced=30
     AThermal_releaseManager; # introduced=30
     AThermal_getCurrentThermalStatus; # introduced=30
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 327b1fb..54538d9 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -125,14 +125,14 @@
     name: "framework-connectivity-ethernet-sources",
     srcs: [
         "src/android/net/EthernetManager.java",
+        "src/android/net/EthernetNetworkManagementException.java",
+        "src/android/net/EthernetNetworkManagementException.aidl",
         "src/android/net/EthernetNetworkSpecifier.java",
+        "src/android/net/EthernetNetworkUpdateRequest.java",
+        "src/android/net/EthernetNetworkUpdateRequest.aidl",
         "src/android/net/IEthernetManager.aidl",
+        "src/android/net/IEthernetNetworkManagementListener.aidl",
         "src/android/net/IEthernetServiceListener.aidl",
-        "src/android/net/IInternalNetworkManagementListener.aidl",
-        "src/android/net/InternalNetworkUpdateRequest.java",
-        "src/android/net/InternalNetworkUpdateRequest.aidl",
-        "src/android/net/InternalNetworkManagementException.java",
-        "src/android/net/InternalNetworkManagementException.aidl",
         "src/android/net/ITetheredInterfaceCallback.aidl",
     ],
     path: "src",
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index ece54df..f472d56 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -320,15 +320,15 @@
     }
 
     private static final class InternalNetworkManagementListener
-            extends IInternalNetworkManagementListener.Stub {
+            extends IEthernetNetworkManagementListener.Stub {
         @NonNull
         private final Executor mExecutor;
         @NonNull
-        private final BiConsumer<Network, InternalNetworkManagementException> mListener;
+        private final BiConsumer<Network, EthernetNetworkManagementException> mListener;
 
         InternalNetworkManagementListener(
                 @NonNull final Executor executor,
-                @NonNull final BiConsumer<Network, InternalNetworkManagementException> listener) {
+                @NonNull final BiConsumer<Network, EthernetNetworkManagementException> listener) {
             Objects.requireNonNull(executor, "Pass a non-null executor");
             Objects.requireNonNull(listener, "Pass a non-null listener");
             mExecutor = executor;
@@ -338,14 +338,14 @@
         @Override
         public void onComplete(
                 @Nullable final Network network,
-                @Nullable final InternalNetworkManagementException e) {
+                @Nullable final EthernetNetworkManagementException e) {
             mExecutor.execute(() -> mListener.accept(network, e));
         }
     }
 
     private InternalNetworkManagementListener getInternalNetworkManagementListener(
             @Nullable final Executor executor,
-            @Nullable final BiConsumer<Network, InternalNetworkManagementException> listener) {
+            @Nullable final BiConsumer<Network, EthernetNetworkManagementException> listener) {
         if (null != listener) {
             Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
         }
@@ -360,9 +360,9 @@
 
     private void updateConfiguration(
             @NonNull String iface,
-            @NonNull InternalNetworkUpdateRequest request,
+            @NonNull EthernetNetworkUpdateRequest request,
             @Nullable @CallbackExecutor Executor executor,
-            @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+            @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
         final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
                 executor, listener);
         try {
@@ -375,7 +375,7 @@
     private void connectNetwork(
             @NonNull String iface,
             @Nullable @CallbackExecutor Executor executor,
-            @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+            @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
         final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
                 executor, listener);
         try {
@@ -388,7 +388,7 @@
     private void disconnectNetwork(
             @NonNull String iface,
             @Nullable @CallbackExecutor Executor executor,
-            @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+            @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
         final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
                 executor, listener);
         try {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
index dcce706..adf9e5a 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
@@ -16,4 +16,4 @@
 
  package android.net;
 
- parcelable InternalNetworkManagementException;
\ No newline at end of file
+ parcelable EthernetNetworkManagementException;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
new file mode 100644
index 0000000..a35f28e
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** @hide */
+public final class EthernetNetworkManagementException
+        extends RuntimeException implements Parcelable {
+
+    /* @hide */
+    public EthernetNetworkManagementException(@NonNull final String errorMessage) {
+        super(errorMessage);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getMessage());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+        final EthernetNetworkManagementException that = (EthernetNetworkManagementException) obj;
+
+        return Objects.equals(getMessage(), that.getMessage());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(getMessage());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<EthernetNetworkManagementException> CREATOR =
+            new Parcelable.Creator<EthernetNetworkManagementException>() {
+                @Override
+                public EthernetNetworkManagementException[] newArray(int size) {
+                    return new EthernetNetworkManagementException[size];
+                }
+
+                @Override
+                public EthernetNetworkManagementException createFromParcel(@NonNull Parcel source) {
+                    return new EthernetNetworkManagementException(source.readString());
+                }
+            };
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
index da00cb9..debc348 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
@@ -16,4 +16,4 @@
 
  package android.net;
 
- parcelable InternalNetworkUpdateRequest;
\ No newline at end of file
+ parcelable EthernetNetworkUpdateRequest;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
similarity index 80%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index f42c4b7..4d229d2 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -23,7 +23,7 @@
 import java.util.Objects;
 
 /** @hide */
-public final class InternalNetworkUpdateRequest implements Parcelable {
+public final class EthernetNetworkUpdateRequest implements Parcelable {
     @NonNull
     private final StaticIpConfiguration mIpConfig;
     @NonNull
@@ -40,7 +40,7 @@
     }
 
     /** @hide */
-    public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+    public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
             @NonNull final NetworkCapabilities networkCapabilities) {
         Objects.requireNonNull(ipConfig);
         Objects.requireNonNull(networkCapabilities);
@@ -48,7 +48,7 @@
         mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
     }
 
-    private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
+    private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
         Objects.requireNonNull(source);
         mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
         mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
@@ -56,7 +56,7 @@
 
     @Override
     public String toString() {
-        return "InternalNetworkUpdateRequest{"
+        return "EthernetNetworkUpdateRequest{"
                 + "mIpConfig=" + mIpConfig
                 + ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
     }
@@ -65,7 +65,7 @@
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o;
+        EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
 
         return Objects.equals(that.getIpConfig(), mIpConfig)
                 && Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
@@ -88,16 +88,16 @@
     }
 
     @NonNull
-    public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR =
-            new Parcelable.Creator<InternalNetworkUpdateRequest>() {
+    public static final Parcelable.Creator<EthernetNetworkUpdateRequest> CREATOR =
+            new Parcelable.Creator<EthernetNetworkUpdateRequest>() {
                 @Override
-                public InternalNetworkUpdateRequest[] newArray(int size) {
-                    return new InternalNetworkUpdateRequest[size];
+                public EthernetNetworkUpdateRequest[] newArray(int size) {
+                    return new EthernetNetworkUpdateRequest[size];
                 }
 
                 @Override
-                public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
-                    return new InternalNetworkUpdateRequest(source);
+                public EthernetNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
+                    return new EthernetNetworkUpdateRequest(source);
                 }
             };
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
index e688bea..544d02b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
@@ -18,8 +18,8 @@
 
 import android.net.IpConfiguration;
 import android.net.IEthernetServiceListener;
-import android.net.IInternalNetworkManagementListener;
-import android.net.InternalNetworkUpdateRequest;
+import android.net.IEthernetNetworkManagementListener;
+import android.net.EthernetNetworkUpdateRequest;
 import android.net.ITetheredInterfaceCallback;
 
 /**
@@ -38,8 +38,8 @@
     void setIncludeTestInterfaces(boolean include);
     void requestTetheredInterface(in ITetheredInterfaceCallback callback);
     void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
-    void updateConfiguration(String iface, in InternalNetworkUpdateRequest request,
-        in IInternalNetworkManagementListener listener);
-    void connectNetwork(String iface, in IInternalNetworkManagementListener listener);
-    void disconnectNetwork(String iface, in IInternalNetworkManagementListener listener);
+    void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request,
+        in IEthernetNetworkManagementListener listener);
+    void connectNetwork(String iface, in IEthernetNetworkManagementListener listener);
+    void disconnectNetwork(String iface, in IEthernetNetworkManagementListener listener);
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
similarity index 80%
rename from packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
index 69cde3b..93edccf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
@@ -16,10 +16,10 @@
 
 package android.net;
 
-import android.net.InternalNetworkManagementException;
+import android.net.EthernetNetworkManagementException;
 import android.net.Network;
 
 /** @hide */
-oneway interface IInternalNetworkManagementListener {
-    void onComplete(in Network network, in InternalNetworkManagementException exception);
+oneway interface IEthernetNetworkManagementListener {
+    void onComplete(in Network network, in EthernetNetworkManagementException exception);
 }
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
deleted file mode 100644
index 7f4e403..0000000
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 android.net;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public final class InternalNetworkManagementException
-        extends RuntimeException implements Parcelable {
-
-    /* @hide */
-    public InternalNetworkManagementException(@NonNull final Throwable t) {
-        super(t);
-    }
-
-    private InternalNetworkManagementException(@NonNull final Parcel source) {
-        super(source.readString());
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(getCause().getMessage());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<InternalNetworkManagementException> CREATOR =
-            new Parcelable.Creator<InternalNetworkManagementException>() {
-                @Override
-                public InternalNetworkManagementException[] newArray(int size) {
-                    return new InternalNetworkManagementException[size];
-                }
-
-                @Override
-                public InternalNetworkManagementException createFromParcel(@NonNull Parcel source) {
-                    return new InternalNetworkManagementException(source);
-                }
-            };
-}
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 93e3dee..29a1831 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -647,6 +647,11 @@
         <item>disabled</item>
     </array>
 
+    <!-- Images offered as options in the avatar picker. If populated, the avatar_image_descriptions
+         array must also be populated with a content description for each image. -->
     <array name="avatar_images"/>
 
+    <!-- Content descriptions for each of the images in the avatar_images array. -->
+    <string-array name="avatar_image_descriptions"/>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index b150e01..45253bb 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -28,9 +28,4 @@
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
-
-    <integer-array name="config_supportedDreamComplications">
-    </integer-array>
-    <integer-array name="config_dreamComplicationsEnabledByDefault">
-    </integer-array>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index af6a658..0fe869f 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1562,4 +1562,8 @@
 
     <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
     <string name="avatar_picker_title">Choose a profile picture</string>
+
+    <!-- Content description for a default user icon. [CHAR LIMIT=NONE] -->
+    <string name="default_user_icon_description">Default user icon</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 99e3160..15ca8cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -173,8 +173,10 @@
     }
 
     public BluetoothDevice getActiveDevice() {
-        if (mService == null) return null;
-        return mService.getActiveDevice();
+        if (mBluetoothAdapter == null) return null;
+        final List<BluetoothDevice> activeDevices = mBluetoothAdapter
+                .getActiveDevices(BluetoothProfile.A2DP);
+        return (activeDevices.size() > 0) ? activeDevices.get(0) : null;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index b11bbde..7e5c124 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -132,10 +132,12 @@
     }
 
     public BluetoothDevice getActiveDevice() {
-        if (mService == null) {
+        if (mBluetoothAdapter == null) {
             return null;
         }
-        return mService.getActiveDevice();
+        final List<BluetoothDevice> activeDevices = mBluetoothAdapter
+                .getActiveDevices(BluetoothProfile.HEADSET);
+        return (activeDevices.size() > 0) ? activeDevices.get(0) : null;
     }
 
     public int getAudioState(BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index dc109ca..6f2d4de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -173,8 +173,10 @@
     }
 
     public List<BluetoothDevice> getActiveDevices() {
-        if (mService == null) return new ArrayList<>();
-        return mService.getActiveDevices();
+        if (mBluetoothAdapter == null) {
+            return new ArrayList<>();
+        }
+        return mBluetoothAdapter.getActiveDevices(BluetoothProfile.HEARING_AID);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 209507a..db6d41e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -21,12 +21,12 @@
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
-import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
@@ -177,10 +177,10 @@
     }
 
     public List<BluetoothDevice> getActiveDevices() {
-        if (mService == null) {
+        if (mBluetoothAdapter == null) {
             return new ArrayList<>();
         }
-        return mService.getActiveDevices();
+        return mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 6bf43e5..a000c09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -151,13 +151,13 @@
                 .map(ComponentName::unflattenFromString)
                 .collect(Collectors.toSet());
 
-        mSupportedComplications =
-                Arrays.stream(resources.getIntArray(R.array.config_supportedDreamComplications))
-                        .boxed()
-                        .collect(Collectors.toSet());
+        mSupportedComplications = Arrays.stream(resources.getIntArray(
+                        com.android.internal.R.array.config_supportedDreamComplications))
+                .boxed()
+                .collect(Collectors.toSet());
 
-        mDefaultEnabledComplications = Arrays.stream(
-                        resources.getIntArray(R.array.config_dreamComplicationsEnabledByDefault))
+        mDefaultEnabledComplications = Arrays.stream(resources.getIntArray(
+                        com.android.internal.R.array.config_dreamComplicationsEnabledByDefault))
                 .boxed()
                 // A complication can only be enabled by default if it is also supported.
                 .filter(mSupportedComplications::contains)
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
index 50015e6..93be66a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -45,6 +45,7 @@
 import com.google.android.setupdesign.util.ThemeHelper;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -180,6 +181,7 @@
         private final int mPreselectedImageStartPosition;
 
         private final List<Drawable> mImageDrawables;
+        private final List<String> mImageDescriptions;
         private final TypedArray mPreselectedImages;
         private final int[] mUserIconColors;
         private int mSelectedPosition = NONE;
@@ -196,6 +198,7 @@
             mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images);
             mUserIconColors = UserIcons.getUserIconColors(getResources());
             mImageDrawables = buildDrawableList();
+            mImageDescriptions = buildDescriptionsList();
         }
 
         @NonNull
@@ -210,15 +213,24 @@
         public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) {
             if (position == mTakePhotoPosition) {
                 viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled));
+                viewHolder.setContentDescription(getString(R.string.user_image_take_photo));
                 viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto());
 
             } else if (position == mChoosePhotoPosition) {
                 viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled));
+                viewHolder.setContentDescription(getString(R.string.user_image_choose_photo));
                 viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto());
 
             } else if (position >= mPreselectedImageStartPosition) {
+                int index = indexFromPosition(position);
                 viewHolder.setSelected(position == mSelectedPosition);
-                viewHolder.setDrawable(mImageDrawables.get(indexFromPosition(position)));
+                viewHolder.setDrawable(mImageDrawables.get(index));
+                if (mImageDescriptions != null) {
+                    viewHolder.setContentDescription(mImageDescriptions.get(index));
+                } else {
+                    viewHolder.setContentDescription(
+                            getString(R.string.default_user_icon_description));
+                }
                 viewHolder.setClickListener(view -> {
                     if (mSelectedPosition == position) {
                         deselect(position);
@@ -256,6 +268,15 @@
             return result;
         }
 
+        private List<String> buildDescriptionsList() {
+            if (mPreselectedImages.length() > 0) {
+                return Arrays.asList(
+                        getResources().getStringArray(R.array.avatar_image_descriptions));
+            }
+
+            return null;
+        }
+
         private Drawable circularDrawableFrom(BitmapDrawable drawable) {
             Bitmap bitmap = drawable.getBitmap();
 
@@ -323,6 +344,10 @@
             mImageView.setImageDrawable(drawable);
         }
 
+        public void setContentDescription(String desc) {
+            mImageView.setContentDescription(desc);
+        }
+
         public void setClickListener(View.OnClickListener listener) {
             mImageView.setOnClickListener(listener);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index f167721..d7b366e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -60,6 +60,8 @@
     private BluetoothDevice mDevice;
     @Mock
     private BluetoothA2dp mBluetoothA2dp;
+    @Mock
+    private BluetoothAdapter mBluetoothAdapter;
     private BluetoothProfile.ServiceListener mServiceListener;
 
     private A2dpProfile mProfile;
@@ -72,7 +74,8 @@
         mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager);
         mServiceListener = mShadowBluetoothAdapter.getServiceListener();
         mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
-        when(mBluetoothA2dp.getActiveDevice()).thenReturn(mDevice);
+        when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP)))
+                .thenReturn(Arrays.asList(mDevice));
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 53d4653..86f7850 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -24,8 +24,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 
-import com.android.settingslib.R;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -53,10 +51,15 @@
 
         final Resources res = mock(Resources.class);
         when(mContext.getResources()).thenReturn(res);
-        when(res.getIntArray(R.array.config_supportedDreamComplications)).thenReturn(
+        when(res.getIntArray(
+                com.android.internal.R.array.config_supportedDreamComplications)).thenReturn(
                 SUPPORTED_DREAM_COMPLICATIONS);
-        when(res.getIntArray(R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
+        when(res.getIntArray(
+                com.android.internal.R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
                 DEFAULT_DREAM_COMPLICATIONS);
+        when(res.getStringArray(
+                com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
+                new String[]{});
         mBackend = new DreamBackend(mContext);
     }
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 27fc6ba..ca90fbe 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -192,6 +192,9 @@
     <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
+    <!-- Permission required for processes that don't own the focused window to switch
+         touch mode state -->
+    <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" />
     <!-- Permission required to test onPermissionsChangedListener -->
     <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
     <uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5989381..776a511 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -308,6 +308,7 @@
 
     <!-- To change system language (HDMI CEC) -->
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" />
 
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
@@ -872,11 +873,11 @@
             android:singleUser="true"
             android:permission="android.permission.BIND_DREAM_SERVICE" />
 
-        <!-- Service for external clients to do media transfer -->
-        <!-- TODO(b/203800643): Export and guard with a permission. -->
+        <!-- Service for external clients to notify us of nearby media devices -->
+        <!-- TODO(b/216313420): Export and guard with a permission. -->
         <service
-            android:name=".media.taptotransfer.sender.MediaTttSenderService"
-           />
+            android:name=".media.nearby.NearbyMediaDevicesService"
+            />
 
         <receiver
             android:name=".tuner.TunerService$ClearReceiver"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index dee4ff5..9722b1f 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   // Looking for unit test presubmit configuration?
   // This currently lives in ATP config apct/system_ui/unit_test
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "PlatformScenarioTests",
       "options": [
@@ -24,7 +24,9 @@
             "exclude-annotation": "android.platform.test.scenario.annotation.FoldableOnly"
         }
       ]
-    },
+    }
+  ],
+  "presubmit": [
     {
       "name": "SystemUIGoogleTests",
       "options": [
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index ecb3cb3..339cab4 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -19,8 +19,10 @@
 <com.android.systemui.qs.FooterActionsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="48dp"
-    android:gravity="center_vertical">
+    android:layout_height="@dimen/qs_footer_height"
+    android:gravity="center_vertical"
+    android:layout_gravity="bottom"
+>
 
     <com.android.systemui.statusbar.phone.MultiUserSwitch
         android:id="@+id/multi_user_switch"
diff --git a/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml b/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
new file mode 100644
index 0000000..95bdd89
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+-->
+
+<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
+<com.android.systemui.qs.FooterActionsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_footer_height"
+    android:gravity="center_vertical"
+    android:layout_gravity="bottom"
+>
+
+    <View
+        android:layout_height="1dp"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        />
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        >
+
+        <com.android.systemui.statusbar.phone.MultiUserSwitch
+            android:id="@+id/multi_user_switch"
+            android:layout_width="@dimen/qs_footer_action_button_size"
+            android:layout_height="@dimen/qs_footer_action_button_size"
+            android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+            android:background="@drawable/qs_footer_action_circle"
+            android:focusable="true">
+
+            <ImageView
+                android:id="@+id/multi_user_avatar"
+                android:layout_width="@dimen/multi_user_avatar_expanded_size"
+                android:layout_height="@dimen/multi_user_avatar_expanded_size"
+                android:layout_gravity="center"
+                android:scaleType="centerInside" />
+        </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+            android:id="@+id/settings_button_container"
+            android:layout_width="@dimen/qs_footer_action_button_size"
+            android:layout_height="@dimen/qs_footer_action_button_size"
+            android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+            android:background="@drawable/qs_footer_action_circle"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+
+            <com.android.systemui.statusbar.phone.SettingsButton
+                android:id="@+id/settings_button"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/qs_footer_action_button_size"
+                android:layout_gravity="center"
+                android:background="@android:color/transparent"
+                android:contentDescription="@string/accessibility_quick_settings_settings"
+                android:padding="@dimen/qs_footer_icon_padding"
+                android:scaleType="centerInside"
+                android:src="@drawable/ic_settings"
+                android:tint="?android:attr/textColorPrimary" />
+
+            <com.android.systemui.statusbar.AlphaOptimizedImageView
+                android:id="@+id/tuner_icon"
+                android:layout_width="8dp"
+                android:layout_height="8dp"
+                android:layout_gravity="center_horizontal|bottom"
+                android:layout_marginBottom="@dimen/qs_footer_icon_padding"
+                android:src="@drawable/tuner"
+                android:tint="?android:attr/textColorTertiary"
+                android:visibility="invisible" />
+
+        </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/pm_lite"
+            android:layout_width="@dimen/qs_footer_action_button_size"
+            android:layout_height="@dimen/qs_footer_action_button_size"
+            android:background="@drawable/qs_footer_action_circle_color"
+            android:clickable="true"
+            android:clipToPadding="false"
+            android:focusable="true"
+            android:padding="@dimen/qs_footer_icon_padding"
+            android:src="@*android:drawable/ic_lock_power_off"
+            android:contentDescription="@string/accessibility_quick_settings_power_menu"
+            android:tint="?androidprv:attr/textColorOnAccent" />
+
+    </LinearLayout>
+</com.android.systemui.qs.FooterActionsView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/action_chip_background.xml b/packages/SystemUI/res/drawable/action_chip_background.xml
index eeff39b..745470f 100644
--- a/packages/SystemUI/res/drawable/action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_background.xml
@@ -17,11 +17,11 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:color="@color/screenshot_button_ripple">
+    android:color="@color/overlay_button_ripple">
     <item android:id="@android:id/background">
         <shape android:shape="rectangle">
             <solid android:color="?androidprv:attr/colorAccentSecondary"/>
-            <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+            <corners android:radius="@dimen/overlay_button_corner_radius"/>
         </shape>
     </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/action_chip_container_background.xml b/packages/SystemUI/res/drawable/action_chip_container_background.xml
index 72767a1..36083f1 100644
--- a/packages/SystemUI/res/drawable/action_chip_container_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_container_background.xml
@@ -19,5 +19,5 @@
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface"/>
-    <corners android:radius="@dimen/screenshot_action_container_corner_radius"/>
+    <corners android:radius="@dimen/overlay_action_container_corner_radius"/>
 </shape>
diff --git a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
index c547c52..ec9465b 100644
--- a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
+++ b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
@@ -20,6 +20,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+        android:pathData="@*android:string/config_work_badge_path_24"
         android:fillColor="?android:attr/colorAccent"/>
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/drawable/ic_media_next.xml
similarity index 61%
rename from packages/SystemUI/res/values-w500dp/dimens.xml
rename to packages/SystemUI/res/drawable/ic_media_next.xml
index 5ce5cee..016653b 100644
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ b/packages/SystemUI/res/drawable/ic_media_next.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
   ~ Copyright (C) 2022 The Android Open Source Project
   ~
@@ -13,9 +12,15 @@
   ~ 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.
+  ~ limitations under the License
   -->
-
-<resources>
-    <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="12dp"
+    android:height="12dp"
+    android:viewportWidth="12"
+    android:viewportHeight="12">
+    <path
+        android:pathData="M0,12L8.5,6L0,0V12ZM2,3.86L5.03,6L2,8.14V3.86ZM12,0H10V12H12V0Z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_media_pause.xml b/packages/SystemUI/res/drawable/ic_media_pause.xml
new file mode 100644
index 0000000..1f4b2cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_pause.xml
@@ -0,0 +1,26 @@
+<?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="14dp"
+    android:height="16dp"
+    android:viewportWidth="14"
+    android:viewportHeight="16">
+    <path
+        android:pathData="M9.1818,15.6363H13.5455V0.3635H9.1818V15.6363ZM0.4546,15.6363H4.8182V0.3635H0.4546V15.6363Z"
+        android:fillColor="#FFFFFF"
+        android:fillType="evenOdd"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/drawable/ic_media_play.xml
similarity index 61%
copy from packages/SystemUI/res/values-w500dp/dimens.xml
copy to packages/SystemUI/res/drawable/ic_media_play.xml
index 5ce5cee..0eac1ad 100644
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ b/packages/SystemUI/res/drawable/ic_media_play.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
   ~ Copyright (C) 2022 The Android Open Source Project
   ~
@@ -13,9 +12,15 @@
   ~ 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.
+  ~ limitations under the License
   -->
-
-<resources>
-    <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:pathData="M20,12L6,21V3L20,12ZM15.26,12L8.55,7.68V16.32L15.26,12Z"
+        android:fillColor="#FFFFFF"
+        android:fillType="evenOdd"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/drawable/ic_media_prev.xml
similarity index 61%
copy from packages/SystemUI/res/values-w500dp/dimens.xml
copy to packages/SystemUI/res/drawable/ic_media_prev.xml
index 5ce5cee..b4aeed4 100644
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ b/packages/SystemUI/res/drawable/ic_media_prev.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
   ~ Copyright (C) 2022 The Android Open Source Project
   ~
@@ -13,9 +12,15 @@
   ~ 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.
+  ~ limitations under the License
   -->
-
-<resources>
-    <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="12dp"
+    android:height="12dp"
+    android:viewportWidth="12"
+    android:viewportHeight="12">
+    <path
+        android:pathData="M0,0H2V12H0V0ZM3.5,6L12,12V0L3.5,6ZM6.97,6L10,8.14V3.86L6.97,6Z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/overlay_actions_background_protection.xml
similarity index 91%
rename from packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
rename to packages/SystemUI/res/drawable/overlay_actions_background_protection.xml
index dd818a0..d8f5632 100644
--- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
+++ b/packages/SystemUI/res/drawable/overlay_actions_background_protection.xml
@@ -17,6 +17,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
     <gradient
         android:angle="90"
-        android:startColor="@color/screenshot_background_protection_start"
+        android:startColor="@color/overlay_background_protection_start"
         android:endColor="#00000000"/>
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_button_background.xml b/packages/SystemUI/res/drawable/overlay_button_background.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/screenshot_button_background.xml
rename to packages/SystemUI/res/drawable/overlay_button_background.xml
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
new file mode 100644
index 0000000..f54c30f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="@dimen/qs_footer_action_inset"
+    android:insetBottom="@dimen/qs_footer_action_inset"
+    android:insetLeft="@dimen/qs_footer_action_inset"
+    android:insetRight="@dimen/qs_footer_action_inset">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item android:id="@android:id/mask">
+            <shape android:shape="oval">
+                <solid android:color="@android:color/white"/>
+            </shape>
+        </item>
+        <item>
+            <shape android:shape="oval">
+                <solid android:color="?attr/offStateColor"/>
+            </shape>
+        </item>
+
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
new file mode 100644
index 0000000..1a323bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="@dimen/qs_footer_action_inset"
+    android:insetBottom="@dimen/qs_footer_action_inset"
+    android:insetLeft="@dimen/qs_footer_action_inset"
+    android:insetRight="@dimen/qs_footer_action_inset">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item android:id="@android:id/mask">
+            <shape android:shape="oval">
+                <solid android:color="@android:color/white"/>
+            </shape>
+        </item>
+        <item>
+            <shape android:shape="oval">
+                <solid android:color="?android:attr/colorAccent"/>
+            </shape>
+        </item>
+
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/qs_media_scrim.xml
similarity index 62%
copy from packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
copy to packages/SystemUI/res/drawable/qs_media_scrim.xml
index dd818a0..2ec319c 100644
--- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
+++ b/packages/SystemUI/res/drawable/qs_media_scrim.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Android Open Source Project
+  ~ 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.
@@ -12,11 +12,15 @@
   ~ 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.
+  ~ limitations under the License
   -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/notification_corner_radius"/>
+    <!-- gradient from 25% in the center to 100% at edges -->
     <gradient
-        android:angle="90"
-        android:startColor="@color/screenshot_background_protection_start"
-        android:endColor="#00000000"/>
-</shape>
\ No newline at end of file
+        android:type="radial"
+        android:gradientRadius="100%p"
+        android:startColor="#40000000"
+        android:endColor="#FF000000" />
+</shape>
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 1e0ce00..0ff1db2 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -18,64 +18,72 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal"
-    android:elevation="@dimen/biometric_dialog_elevation">
+    android:elevation="@dimen/biometric_dialog_elevation"
+    android:orientation="vertical">
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-
-    <TextView
-        android:id="@+id/title"
+    <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Title"/>
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Subtitle"/>
+        <LinearLayout
+            android:id="@+id/auth_credential_header"
+            style="@style/AuthCredentialHeaderStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true">
 
-    <TextView
-        android:id="@+id/description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Description"/>
+            <ImageView
+                android:id="@+id/icon"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:contentDescription="@null" />
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
+            <TextView
+                android:id="@+id/title"
+                style="@style/TextAppearance.AuthNonBioCredential.Title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
-    <ImeAwareEditText
-        android:id="@+id/lockPassword"
-        android:layout_width="208dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:minHeight="48dp"
-        android:gravity="center"
-        android:inputType="textPassword"
-        android:maxLength="500"
-        android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
-        style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
+            <TextView
+                android:id="@+id/subtitle"
+                style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
-    <TextView
-        android:id="@+id/error"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Error"/>
+            <TextView
+                android:id="@+id/description"
+                style="@style/TextAppearance.AuthNonBioCredential.Description"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="5"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:layout_alignParentBottom="true">
+
+            <ImeAwareEditText
+                android:id="@+id/lockPassword"
+                style="@style/TextAppearance.AuthCredential.PasswordEntry"
+                android:layout_width="208dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+                android:inputType="textPassword"
+                android:minHeight="48dp" />
+
+            <TextView
+                android:id="@+id/error"
+                style="@style/TextAppearance.AuthNonBioCredential.Error"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+        </LinearLayout>
+
+    </RelativeLayout>
 
 </com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 4939ea2..dada981 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -22,76 +22,81 @@
     android:gravity="center_horizontal"
     android:elevation="@dimen/biometric_dialog_elevation">
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-
-    <TextView
-        android:id="@+id/title"
+    <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Title"/>
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Subtitle"/>
+        <LinearLayout
+            android:id="@+id/auth_credential_header"
+            style="@style/AuthCredentialHeaderStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
 
-    <TextView
-        android:id="@+id/description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@style/TextAppearance.AuthCredential.Description"/>
+            <ImageView
+                android:id="@+id/icon"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:contentDescription="@null" />
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
+            <TextView
+                android:id="@+id/title"
+                style="@style/TextAppearance.AuthNonBioCredential.Title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:gravity="center"
-        android:paddingLeft="0dp"
-        android:paddingRight="0dp"
-        android:paddingTop="0dp"
-        android:paddingBottom="16dp"
-        android:clipToPadding="false">
+            <TextView
+                android:id="@+id/subtitle"
+                style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
-        <FrameLayout
-            android:layout_width="wrap_content"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            style="@style/LockPatternContainerStyle">
+            <TextView
+                android:id="@+id/description"
+                style="@style/TextAppearance.AuthNonBioCredential.Description"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
 
-            <com.android.internal.widget.LockPatternView
-                android:id="@+id/lockPattern"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                style="@style/LockPatternStyleBiometricPrompt"/>
-
-        </FrameLayout>
-
-        <TextView
-            android:id="@+id/error"
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            style="@style/TextAppearance.AuthCredential.Error"/>
+            android:layout_below="@id/auth_credential_header"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingBottom="16dp"
+            android:paddingTop="60dp">
 
-    </LinearLayout>
+            <FrameLayout
+                style="@style/LockPatternContainerStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="0dp"
+                android:layout_weight="1">
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
+                <com.android.internal.widget.LockPatternView
+                    android:id="@+id/lockPattern"
+                    style="@style/LockPatternStyle"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_gravity="center" />
+
+            </FrameLayout>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true">
+
+            <TextView
+                android:id="@+id/error"
+                style="@style/TextAppearance.AuthNonBioCredential.Error"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+        </LinearLayout>
+
+    </RelativeLayout>
 
 </com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 7e31909..4817d45 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -17,7 +17,7 @@
 <com.android.systemui.clipboardoverlay.DraggableConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:theme="@style/Screenshot"
+    android:theme="@style/FloatingOverlay"
     android:alpha="0"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -28,7 +28,7 @@
         android:layout_width="0dp"
         android:elevation="1dp"
         android:background="@drawable/action_chip_container_background"
-        android:layout_marginStart="@dimen/screenshot_action_container_margin_horizontal"
+        android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
         app:layout_constraintBottom_toBottomOf="@+id/actions_container"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/actions_container"
@@ -37,9 +37,9 @@
         android:id="@+id/actions_container"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal"
-        android:paddingEnd="@dimen/screenshot_action_container_padding_right"
-        android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
+        android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+        android:paddingEnd="@dimen/overlay_action_container_padding_right"
+        android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
         android:elevation="1dp"
         android:scrollbars="none"
         app:layout_constraintHorizontal_bias="0"
@@ -53,9 +53,9 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:animateLayoutChanges="true">
-            <include layout="@layout/screenshot_action_chip"
+            <include layout="@layout/overlay_action_chip"
                      android:id="@+id/remote_copy_chip"/>
-            <include layout="@layout/screenshot_action_chip"
+            <include layout="@layout/overlay_action_chip"
                      android:id="@+id/edit_chip"/>
         </LinearLayout>
     </HorizontalScrollView>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 856697c..cdf6103 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -18,7 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:background="?android:colorBackgroundFloating"
     android:id="@+id/root"
     android:layout_width="match_parent"
@@ -32,7 +31,7 @@
         android:text="@string/save"
         android:layout_marginStart="8dp"
         android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin"
-        android:background="@drawable/screenshot_button_background"
+        android:background="@drawable/overlay_button_background"
         android:textColor="?android:textColorSecondary"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
@@ -46,7 +45,7 @@
         android:text="@android:string/cancel"
         android:layout_marginStart="6dp"
         android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin"
-        android:background="@drawable/screenshot_button_background"
+        android:background="@drawable/overlay_button_background"
         android:textColor="?android:textColorSecondary"
         app:layout_constraintStart_toEndOf="@id/save"
         app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index cc02fea..51d1608 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -28,6 +28,22 @@
     android:background="@drawable/qs_media_background"
     android:theme="@style/MediaPlayer">
 
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="184dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:translationZ="0dp"
+        android:id="@+id/album_art"
+        android:scaleType="centerCrop"
+        android:adjustViewBounds="true"
+        android:clipToOutline="true"
+        android:foreground="@drawable/qs_media_scrim"
+        android:background="@drawable/qs_media_scrim"
+        />
+
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/center_vertical_guideline"
         android:layout_width="wrap_content"
@@ -281,6 +297,7 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintTop_toBottomOf="@id/remove_text">
         <TextView
+            android:id="@+id/cancel_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center|bottom"
@@ -304,6 +321,7 @@
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintTop_toBottomOf="@id/remove_text">
         <TextView
+            android:id="@+id/dismiss_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center|bottom"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index b546a9c..9471b9f 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -264,6 +264,7 @@
         app:layout_constraintTop_toBottomOf="@id/remove_text">
 
         <TextView
+            android:id="@+id/cancel_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center|bottom"
@@ -288,6 +289,7 @@
         app:layout_constraintTop_toBottomOf="@id/remove_text">
 
         <TextView
+            android:id="@+id/dismiss_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center|bottom"
diff --git a/packages/SystemUI/res/layout/screenshot_action_chip.xml b/packages/SystemUI/res/layout/overlay_action_chip.xml
similarity index 61%
rename from packages/SystemUI/res/layout/screenshot_action_chip.xml
rename to packages/SystemUI/res/layout/overlay_action_chip.xml
index b80469f..e0c20ff 100644
--- a/packages/SystemUI/res/layout/screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/overlay_action_chip.xml
@@ -14,33 +14,34 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.screenshot.ScreenshotActionChip
+<com.android.systemui.screenshot.OverlayActionChip
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/screenshot_action_chip"
+    android:id="@+id/overlay_action_chip"
+    android:theme="@style/FloatingOverlay"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginStart="@dimen/screenshot_action_chip_margin_start"
-    android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical"
+    android:layout_marginStart="@dimen/overlay_action_chip_margin_start"
+    android:paddingVertical="@dimen/overlay_action_chip_margin_vertical"
     android:layout_gravity="center"
     android:gravity="center"
     android:alpha="0.0">
     <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
+        android:paddingVertical="@dimen/overlay_action_chip_padding_vertical"
         android:background="@drawable/action_chip_background"
         android:gravity="center">
         <ImageView
-            android:id="@+id/screenshot_action_chip_icon"
-            android:tint="?android:attr/textColorPrimary"
-            android:layout_width="@dimen/screenshot_action_chip_icon_size"
-            android:layout_height="@dimen/screenshot_action_chip_icon_size"/>
+            android:id="@+id/overlay_action_chip_icon"
+            android:tint="?attr/overlayButtonTextColor"
+            android:layout_width="@dimen/overlay_action_chip_icon_size"
+            android:layout_height="@dimen/overlay_action_chip_icon_size"/>
         <TextView
-            android:id="@+id/screenshot_action_chip_text"
+            android:id="@+id/overlay_action_chip_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-            android:textSize="@dimen/screenshot_action_chip_text_size"
-            android:textColor="?android:attr/textColorPrimary"/>
+            android:textSize="@dimen/overlay_action_chip_text_size"
+            android:textColor="?attr/overlayButtonTextColor"/>
     </LinearLayout>
-</com.android.systemui.screenshot.ScreenshotActionChip>
+</com.android.systemui.screenshot.OverlayActionChip>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 5cd9e94..b6e3499 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -19,7 +19,7 @@
 <com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/qs_footer"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/qs_footer_height"
+    android:layout_height="wrap_content"
     android:layout_marginStart="@dimen/qs_footer_margin"
     android:layout_marginEnd="@dimen/qs_footer_margin"
     android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
@@ -36,7 +36,7 @@
 
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="48dp"
+            android:layout_height="@dimen/qs_footer_height"
             android:layout_gravity="center_vertical">
 
             <TextView
@@ -80,8 +80,13 @@
 
         </LinearLayout>
 
-        <include layout="@layout/footer_actions"
-            android:id="@+id/qs_footer_actions"/>
+        <ViewStub
+            android:id="@+id/footer_stub"
+            android:inflatedId="@+id/qs_footer_actions"
+            android:layout="@layout/footer_actions"
+            android:layout_height="@dimen/qs_footer_height"
+            android:layout_width="match_parent"
+        />
 
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index f5c6036..22abd0c 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -51,6 +51,15 @@
         android:id="@+id/qs_detail"
         layout="@layout/qs_detail" />
 
+    <ViewStub
+        android:id="@+id/container_stub"
+        android:inflatedId="@+id/qs_footer_actions"
+        android:layout="@layout/new_footer_actions"
+        android:layout_height="@dimen/qs_footer_height"
+        android:layout_width="match_parent"
+        android:layout_gravity="bottom"
+        />
+
     <include
         android:id="@+id/qs_customize"
         layout="@layout/qs_customize_panel"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 10a2f4c..2c29f07 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -57,16 +57,6 @@
             android:focusable="true"
             android:paddingBottom="24dp"
             android:importantForAccessibility="yes">
-
-            <include
-                layout="@layout/footer_actions"
-                android:id="@+id/qqs_footer_actions"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/qqs_layout_margin_top"
-                android:layout_marginStart="@dimen/qs_footer_margin"
-                android:layout_marginEnd="@dimen/qs_footer_margin"
-                />
         </com.android.systemui.qs.QuickQSPanel>
     </RelativeLayout>
 
diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml
index 227212b..890dbe5 100644
--- a/packages/SystemUI/res/layout/screenshot.xml
+++ b/packages/SystemUI/res/layout/screenshot.xml
@@ -17,7 +17,7 @@
 <com.android.systemui.screenshot.ScreenshotView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/screenshot_frame"
-    android:theme="@style/Screenshot"
+    android:theme="@style/FloatingOverlay"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:importantForAccessibility="no">
@@ -30,11 +30,11 @@
         android:importantForAccessibility="no"/>
     <ImageView
         android:id="@+id/screenshot_actions_background"
-        android:layout_height="@dimen/screenshot_bg_protection_height"
+        android:layout_height="@dimen/overlay_bg_protection_height"
         android:layout_width="match_parent"
         android:layout_gravity="bottom"
         android:alpha="0.0"
-        android:src="@drawable/screenshot_actions_background_protection"/>
+        android:src="@drawable/overlay_actions_background_protection"/>
     <ImageView
         android:id="@+id/screenshot_flash"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 8f791c3..813bb60 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -26,7 +26,7 @@
         android:layout_width="0dp"
         android:elevation="1dp"
         android:background="@drawable/action_chip_container_background"
-        android:layout_marginStart="@dimen/screenshot_action_container_margin_horizontal"
+        android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
         app:layout_constraintBottom_toBottomOf="@+id/screenshot_actions_container"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/screenshot_actions_container"
@@ -35,9 +35,9 @@
         android:id="@+id/screenshot_actions_container"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal"
-        android:paddingEnd="@dimen/screenshot_action_container_padding_right"
-        android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
+        android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+        android:paddingEnd="@dimen/overlay_action_container_padding_right"
+        android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
         android:elevation="1dp"
         android:scrollbars="none"
         app:layout_constraintHorizontal_bias="0"
@@ -50,11 +50,11 @@
             android:id="@+id/screenshot_actions"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content">
-            <include layout="@layout/screenshot_action_chip"
+            <include layout="@layout/overlay_action_chip"
                      android:id="@+id/screenshot_share_chip"/>
-            <include layout="@layout/screenshot_action_chip"
+            <include layout="@layout/overlay_action_chip"
                      android:id="@+id/screenshot_edit_chip"/>
-            <include layout="@layout/screenshot_action_chip"
+            <include layout="@layout/overlay_action_chip"
                      android:id="@+id/screenshot_scroll_chip"
                      android:visibility="gone" />
         </LinearLayout>
@@ -89,7 +89,7 @@
     <ImageView
         android:id="@+id/screenshot_preview"
         android:visibility="invisible"
-        android:layout_width="@dimen/screenshot_x_scale"
+        android:layout_width="@dimen/overlay_x_scale"
         android:layout_margin="@dimen/overlay_border_width"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3412722..b318bbc 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -58,9 +58,9 @@
     <!-- The color of the text in the Global Actions menu -->
     <color name="global_actions_alert_text">@color/GM2_red_300</color>
 
-    <!-- Global screenshot actions -->
-    <color name="screenshot_button_ripple">#42FFFFFF</color>
-    <color name="screenshot_background_protection_start">#80000000</color> <!-- 50% black -->
+    <!-- Floating overlay actions -->
+    <color name="overlay_button_ripple">#42FFFFFF</color>
+    <color name="overlay_background_protection_start">#80000000</color> <!-- 50% black -->
 
     <!-- Media -->
     <color name="media_divider">#85ffffff</color>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 1f815b7..f7261e7 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -47,8 +47,8 @@
         <item name="android:textColorSecondary">?android:attr/textColorPrimaryInverse</item>
     </style>
 
-    <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight">
-        <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
+    <style name="FloatingOverlay" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="overlayButtonTextColor">?android:attr/textColorPrimaryInverse</item>
     </style>
 
     <style name="Theme.PeopleTileConfigActivity" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 219fd43..ae557c4 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <!--
  * Copyright (c) 2022, The Android Open Source Project
  *
@@ -16,6 +17,7 @@
 */
 -->
 <resources>
+    <dimen name="controls_padding_horizontal">205dp</dimen>
     <dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen>
     <dimen name="notification_panel_margin_bottom">56dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 1564ee8..95df594 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -16,8 +16,9 @@
 */
 -->
 <resources>
-
     <!-- gap on either side of status bar notification icons -->
     <dimen name="status_bar_icon_padding">1dp</dimen>
+
+    <dimen name="controls_padding_horizontal">75dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-w850dp/dimens.xml b/packages/SystemUI/res/values-w850dp/dimens.xml
deleted file mode 100644
index bb6ba8fb..0000000
--- a/packages/SystemUI/res/values-w850dp/dimens.xml
+++ /dev/null
@@ -1,21 +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.
-  -->
-
-<resources>
-    <dimen name="controls_padding_horizontal">205dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index de136de..e6ab0ff 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -204,5 +204,7 @@
         <attr name="singleLineVerticalPadding" format="dimension" />
         <attr name="textViewId" format="reference" />
     </declare-styleable>
+
+    <attr name="overlayButtonTextColor" format="color" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 81e3e04..3ab569a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -138,9 +138,9 @@
     <color name="udfps_enroll_progress">#7DA7F1</color>
     <color name="udfps_enroll_progress_help">#ffEE675C</color>
 
-    <!-- Global screenshot actions -->
-    <color name="screenshot_button_ripple">#1f000000</color>
-    <color name="screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
+    <!-- Floating overlay actions -->
+    <color name="overlay_button_ripple">#1f000000</color>
+    <color name="overlay_background_protection_start">#40000000</color> <!-- 25% black -->
 
     <!-- GM2 colors -->
     <color name="GM2_grey_100">#F1F3F4</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bba616f..dba7290 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -258,36 +258,36 @@
 
     <!-- Dimensions related to screenshots -->
 
-    <!-- The padding on the global screenshot background image -->
-    <dimen name="screenshot_x_scale">80dp</dimen>
-    <dimen name="screenshot_bg_protection_height">242dp</dimen>
-    <dimen name="screenshot_action_container_corner_radius">18dp</dimen>
-    <dimen name="screenshot_action_container_padding_vertical">4dp</dimen>
-    <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
-    <dimen name="screenshot_action_container_padding_right">8dp</dimen>
-    <!-- Radius of the chip background on global screenshot actions -->
-    <dimen name="screenshot_button_corner_radius">8dp</dimen>
-    <!-- Margin between successive chips -->
-    <dimen name="screenshot_action_chip_margin_start">8dp</dimen>
-    <!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
-    <dimen name="screenshot_action_chip_margin_vertical">4dp</dimen>
-    <dimen name="screenshot_action_chip_padding_vertical">11dp</dimen>
-    <dimen name="screenshot_action_chip_icon_size">18sp</dimen>
-    <!-- Padding on each side of the icon for icon-only chips -->
-    <dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen>
-    <!-- Padding at the edges of the chip for icon-and-text chips -->
-    <dimen name="screenshot_action_chip_padding_horizontal">12dp</dimen>
-    <!-- Spacing between chip icon and chip text -->
-    <dimen name="screenshot_action_chip_spacing">8dp</dimen>
-    <dimen name="screenshot_action_chip_text_size">14sp</dimen>
-    <dimen name="screenshot_dismissal_height_delta">80dp</dimen>
+
     <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
     <dimen name="long_screenshot_action_bar_top_margin">8dp</dimen>
 
     <!-- Dimensions shared between "overlays" (clipboard and screenshot preview UIs) -->
+    <!-- Constrained size of the floating overlay preview -->
+    <dimen name="overlay_x_scale">80dp</dimen>
+    <!-- Radius of the chip background on floating overlay actions -->
+    <dimen name="overlay_button_corner_radius">8dp</dimen>
+    <!-- Margin between successive chips -->
+    <dimen name="overlay_action_chip_margin_start">8dp</dimen>
+    <!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
+    <dimen name="overlay_action_chip_margin_vertical">4dp</dimen>
+    <dimen name="overlay_action_chip_padding_vertical">11dp</dimen>
+    <dimen name="overlay_action_chip_icon_size">18sp</dimen>
+    <!-- Padding on each side of the icon for icon-only chips -->
+    <dimen name="overlay_action_chip_icon_only_padding_horizontal">14dp</dimen>
+    <!-- Padding at the edges of the chip for icon-and-text chips -->
+    <dimen name="overlay_action_chip_padding_horizontal">12dp</dimen>
+    <!-- Spacing between chip icon and chip text -->
+    <dimen name="overlay_action_chip_spacing">8dp</dimen>
+    <dimen name="overlay_action_chip_text_size">14sp</dimen>
     <dimen name="overlay_offset_y">8dp</dimen>
     <dimen name="overlay_offset_x">16dp</dimen>
     <dimen name="overlay_preview_elevation">4dp</dimen>
+    <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
+    <dimen name="overlay_bg_protection_height">242dp</dimen>
+    <dimen name="overlay_action_container_corner_radius">18dp</dimen>
+    <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
+    <dimen name="overlay_action_container_padding_right">8dp</dimen>
     <dimen name="overlay_dismiss_button_elevation">7dp</dimen>
     <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen>
     <dimen name="overlay_dismiss_button_margin">8dp</dimen>
@@ -295,7 +295,7 @@
     <!-- need a negative margin for some of the constraints. should be overlay_border_width * -1 -->
     <dimen name="overlay_border_width_neg">-4dp</dimen>
 
-    <dimen name="clipboard_preview_size">@dimen/screenshot_x_scale</dimen>
+    <dimen name="clipboard_preview_size">@dimen/overlay_x_scale</dimen>
 
 
     <!-- The width of the view containing navigation buttons -->
@@ -327,7 +327,7 @@
 
     <!-- The height of the quick settings footer that holds the user switcher, settings icon,
          etc. -->
-    <dimen name="qs_footer_height">96dp</dimen>
+    <dimen name="qs_footer_height">48dp</dimen>
 
     <!-- The size of each of the icon buttons in the QS footer -->
     <dimen name="qs_footer_action_button_size">48dp</dimen>
@@ -491,7 +491,7 @@
     <dimen name="qs_tile_text_size">14sp</dimen>
     <dimen name="qs_panel_padding">16dp</dimen>
     <dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
-    <dimen name="qs_panel_padding_bottom">0dp</dimen>
+    <dimen name="qs_panel_padding_bottom">@dimen/qs_footer_height</dimen>
     <dimen name="qs_panel_padding_top">48dp</dimen>
     <dimen name="qs_detail_header_padding">0dp</dimen>
     <dimen name="qs_detail_image_width">56dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 57f1f3f..590cc9b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -236,6 +236,41 @@
         <item name="android:textColor">?android:attr/colorError</item>
     </style>
 
+    <style name="TextAppearance.AuthNonBioCredential"
+        parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:accessibilityLiveRegion">polite</item>
+        <item name="android:textAlignment">gravity</item>
+        <item name="android:layout_gravity">top</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.AuthNonBioCredential.Title">
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:layout_marginTop">20dp</item>
+        <item name="android:textSize">36sp</item>
+    </style>
+
+    <style name="TextAppearance.AuthNonBioCredential.Subtitle">
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:layout_marginTop">20dp</item>
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <style name="TextAppearance.AuthNonBioCredential.Description">
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:layout_marginTop">20dp</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="TextAppearance.AuthNonBioCredential.Error">
+        <item name="android:paddingTop">6dp</item>
+        <item name="android:paddingBottom">18dp</item>
+        <item name="android:paddingHorizontal">24dp</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/colorError</item>
+        <item name="android:gravity">center</item>
+    </style>
+
     <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:gravity">center</item>
         <item name="android:singleLine">true</item>
@@ -243,6 +278,15 @@
         <item name="android:textSize">24sp</item>
     </style>
 
+    <style name="AuthCredentialHeaderStyle">
+        <item name="android:paddingStart">48dp</item>
+        <item name="android:paddingEnd">24dp</item>
+        <item name="android:paddingTop">28dp</item>
+        <item name="android:paddingBottom">20dp</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:layout_gravity">top</item>
+    </style>
+
     <style name="DeviceManagementDialogTitle">
         <item name="android:gravity">center</item>
         <item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
@@ -307,9 +351,8 @@
         <item name="android:maxWidth">420dp</item>
         <item name="android:minHeight">0dp</item>
         <item name="android:minWidth">0dp</item>
-        <item name="android:paddingBottom">0dp</item>
-        <item name="android:paddingHorizontal">44dp</item>
-        <item name="android:paddingTop">0dp</item>
+        <item name="android:paddingHorizontal">60dp</item>
+        <item name="android:paddingBottom">40dp</item>
     </style>
 
     <style name="LockPatternStyle">
@@ -664,7 +707,9 @@
         <item name="android:windowActivityTransitions">true</item>
     </style>
 
-    <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight"/>
+    <style name="FloatingOverlay" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="overlayButtonTextColor">?android:attr/textColorPrimary</item>
+    </style>
 
     <!-- Clipboard overlay's edit text activity. -->
     <style name="EditTextActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
index 9010d51..fc6bb50 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -42,7 +42,9 @@
      * are different than actual bounds (e.g. view container may
      * have larger width than width of the items in the container)
      */
-    private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {}
+    private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {},
+    /** Allows to set the alpha based on the progress. */
+    private val alphaProvider: AlphaProvider? = null
 ) : UnfoldTransitionProgressProvider.TransitionProgressListener {
 
     private val screenSize = Point()
@@ -99,17 +101,27 @@
 
     override fun onTransitionProgress(progress: Float) {
         animatedViews.forEach {
-            it.view.get()?.let { view ->
-                translationApplier.apply(
-                    view = view,
-                    x = it.startTranslationX * (1 - progress),
-                    y = it.startTranslationY * (1 - progress)
-                )
-            }
+            it.applyTransition(progress)
+            it.applyAlpha(progress)
         }
         lastAnimationProgress = progress
     }
 
+    private fun AnimatedView.applyTransition(progress: Float) {
+        view.get()?.let { view ->
+            translationApplier.apply(
+                view = view,
+                x = startTranslationX * (1 - progress),
+                y = startTranslationY * (1 - progress)
+            )
+        }
+    }
+
+    private fun AnimatedView.applyAlpha(progress: Float) {
+        if (alphaProvider == null) return
+        view.get()?.alpha = alphaProvider.getAlpha(progress)
+    }
+
     private fun createAnimatedView(view: View): AnimatedView =
         AnimatedView(view = WeakReference(view)).updateAnimatedView(view)
 
@@ -146,6 +158,13 @@
         }
     }
 
+    /** Allows to set a custom alpha based on the progress. */
+    interface AlphaProvider {
+
+        /** Returns the alpha views should have at a given progress. */
+        fun getAlpha(progress: Float): Float
+    }
+
     /**
      * Interface that allows to use custom logic to get the center of the view
      */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl
new file mode 100644
index 0000000..6db06f0
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.media;
+
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback;
+import com.android.systemui.shared.media.NearbyDevice;
+
+/**
+ * An interface that provides information about nearby devices that are able to play media.
+ *
+ * External clients will implement this interface and System UI will invoke it if it's passed to
+ * SystemUI via {@link INearbyMediaDevicesService.registerProvider}.
+ */
+interface INearbyMediaDevicesProvider {
+  /**
+   * Returns a list of nearby devices that are able to play media.
+   */
+  List<NearbyDevice> getCurrentNearbyDevices() = 1;
+
+  /**
+   * Registers a callback that will be notified each time the status of a nearby device changes.
+   */
+  oneway void registerNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 2;
+
+  /**
+   * Unregisters a callback. See {@link registerNearbyDevicesCallback}.
+   */
+  oneway void unregisterNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 3;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl
new file mode 100644
index 0000000..4f3e10d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.media;
+
+import com.android.systemui.shared.media.INearbyMediaDevicesProvider;
+
+/**
+ * An interface that can be invoked to notify System UI of nearby media devices.
+ *
+ * External clients wanting to notify System UI about the status of nearby media devices should
+ * implement {@link INearbyMediaDevicesProvider} and then register it with system UI using this
+ * service.
+ *
+ * System UI will implement this interface and external clients will invoke it.
+ */
+interface INearbyMediaDevicesService {
+  /** Registers a new provider. */
+  oneway void registerProvider(INearbyMediaDevicesProvider provider) = 1;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl
new file mode 100644
index 0000000..a835f52
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl
@@ -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.shared.media;
+
+/**
+ * A callback used to notify implementors of changes in the status of nearby devices that are able
+ * to play media.
+ *
+ * External clients may allow registration of these callbacks and external clients will be
+ * responsible for notifying the callbacks appropriately. System UI is only a mediator between the
+ * external client and these callbacks.
+ */
+interface INearbyMediaDevicesUpdateCallback {
+    /** Unknown distance range. */
+    const int RANGE_UNKNOWN = 0;
+    /** Distance is very far away from the peer device. */
+    const int RANGE_FAR = 1;
+    /** Distance is relatively long from the peer device, typically a few meters. */
+    const int RANGE_LONG = 2;
+    /** Distance is close to the peer device, typically with one or two meter. */
+    const int RANGE_CLOSE = 3;
+    /** Distance is very close to the peer device, typically within one meter or less. */
+    const int RANGE_WITHIN_REACH = 4;
+
+    /** Invoked by external clients when media device changes are detected. */
+    oneway void nearbyDeviceUpdate(in String routeId, in int rangeZone) = 1;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl
similarity index 89%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
rename to packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl
index 861a4ed..62b50ed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.mediattt;
+package com.android.systemui.shared.media;
 
-parcelable DeviceInfo;
+parcelable NearbyDevice;
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt
rename to packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt
index 96b853f..9cab3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.media.nearby
+package com.android.systemui.shared.media
 
 import android.os.Parcel
 import android.os.Parcelable
@@ -26,14 +26,15 @@
  *   - [routeId] identifying the media route
  *   - [rangeZone] specifying how far away the device with the media route is from this device.
  */
-class NearbyDevice(parcel: Parcel) : Parcelable {
-    var routeId: String? = null
+class NearbyDevice(
+    val routeId: String?,
     @RangeZone val rangeZone: Int
+) : Parcelable {
 
-    init {
-        routeId = parcel.readString() ?: "unknown"
+    private constructor(parcel: Parcel) : this(
+        routeId = parcel.readString() ?: null,
         rangeZone = parcel.readInt()
-    }
+    )
 
     override fun describeContents() = 0
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt
new file mode 100644
index 0000000..b5eaff6
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.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.shared.media
+
+import androidx.annotation.IntDef
+import kotlin.annotation.AnnotationRetention
+
+@IntDef(
+    INearbyMediaDevicesUpdateCallback.RANGE_UNKNOWN,
+    INearbyMediaDevicesUpdateCallback.RANGE_FAR,
+    INearbyMediaDevicesUpdateCallback.RANGE_LONG,
+    INearbyMediaDevicesUpdateCallback.RANGE_CLOSE,
+    INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
+)
+@Retention(AnnotationRetention.SOURCE)
+/** The various range zones a device can be in, in relation to the current device. */
+annotation class RangeZone
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
deleted file mode 100644
index d41aaf3..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.mediattt
-
-import android.os.Parcel
-import android.os.Parcelable
-
-/**
- * Represents a device that can send or receive media. Includes any device information necessary for
- * SysUI to display an informative chip to the user.
- */
-class DeviceInfo(val name: String) : Parcelable {
-    constructor(parcel: Parcel) : this(parcel.readString())
-
-    override fun writeToParcel(dest: Parcel?, flags: Int) {
-        dest?.writeString(name)
-    }
-
-    override fun describeContents() = 0
-
-    override fun toString() = "name: $name"
-
-    companion object CREATOR : Parcelable.Creator<DeviceInfo> {
-        override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel)
-        override fun newArray(size: Int) = arrayOfNulls<DeviceInfo?>(size)
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
deleted file mode 100644
index eb1c9d0..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.mediattt;
-
-import android.media.MediaRoute2Info;
-import com.android.systemui.shared.mediattt.DeviceInfo;
-import com.android.systemui.shared.mediattt.IUndoTransferCallback;
-
-/**
- * An interface that can be invoked to trigger media transfer events on System UI.
- *
- * This interface is for the *sender* device, which is the device currently playing media. This
- * sender device can transfer the media to a different device, called the receiver.
- *
- * System UI will implement this interface and other services will invoke it.
- */
-interface IDeviceSenderService {
-    /**
-     * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
-     * the user can potentially *start* a cast to the receiver device if the user moves their device
-     * a bit closer.
-     *
-     * Important notes:
-     *   - When this callback triggers, the device is close enough to inform the user that
-     *     transferring is an option, but the device is *not* close enough to actually initiate a
-     *     transfer yet.
-     *   - This callback is for *starting* a cast. It should be used when this device is currently
-     *     playing media locally and the media should be transferred to be played on the receiver
-     *     device instead.
-     */
-    oneway void closeToReceiverToStartCast(
-        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
-    /**
-     * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
-     * the user can potentially *end* a cast on the receiver device if the user moves this device a
-     * bit closer.
-     *
-     * Important notes:
-     *   - When this callback triggers, the device is close enough to inform the user that
-     *     transferring is an option, but the device is *not* close enough to actually initiate a
-     *     transfer yet.
-     *   - This callback is for *ending* a cast. It should be used when media is currently being
-     *     played on the receiver device and the media should be transferred to play locally
-     *     instead.
-     */
-    oneway void closeToReceiverToEndCast(
-        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
-    /**
-     * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
-     * device has been started.
-     *
-     * Important notes:
-     *   - This callback is for *starting* a cast. It should be used when this device is currently
-     *     playing media locally and the media has started being transferred to the receiver device
-     *     instead.
-     */
-    oneway void transferToReceiverTriggered(
-        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
-    /**
-     * Invoke to notify System UI that a media transfer from the receiver and back to this device
-     * (the sender) has been started.
-     *
-     * Important notes:
-     *   - This callback is for *ending* a cast. It should be used when media is currently being
-     *     played on the receiver device and the media has started being transferred to play locally
-     *     instead.
-     */
-    oneway void transferToThisDeviceTriggered(
-        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
-    /**
-     * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
-     * device has finished successfully.
-     *
-     * Important notes:
-     *   - This callback is for *starting* a cast. It should be used when this device had previously
-     *     been playing media locally and the media has successfully been transferred to the
-     *     receiver device instead.
-     *
-     * @param undoCallback will be invoked if the user chooses to undo this transfer.
-     */
-    oneway void transferToReceiverSucceeded(
-        in MediaRoute2Info mediaInfo,
-        in DeviceInfo otherDeviceInfo,
-        in IUndoTransferCallback undoCallback);
-
-    /**
-     * Invoke to notify System UI that a media transfer from the receiver and back to this device
-     * (the sender) has finished successfully.
-     *
-     * Important notes:
-     *   - This callback is for *ending* a cast. It should be used when media was previously being
-     *     played on the receiver device and has been successfully transferred to play locally on
-     *     this device instead.
-     *
-     * @param undoCallback will be invoked if the user chooses to undo this transfer.
-     */
-    oneway void transferToThisDeviceSucceeded(
-        in MediaRoute2Info mediaInfo,
-        in DeviceInfo otherDeviceInfo,
-        in IUndoTransferCallback undoCallback);
-
-    /**
-     * Invoke to notify System UI that the attempted transfer has failed.
-     *
-     * This callback will be used for both the transfer that should've *started* playing the media
-     * on the receiver and the transfer that should've *ended* the playing on the receiver.
-     */
-    oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
-    /**
-     * Invoke to notify System UI that this device is no longer close to the receiver device.
-     */
-    oneway void noLongerCloseToReceiver(
-        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 20d6e32..881e6a9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -120,6 +120,13 @@
             AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9
 
     /**
+     * Action ID to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and
+     * play/stop media
+     */
+    private static final int SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK =
+            AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK; // = 10
+
+    /**
      * Action ID to trigger the accessibility button
      */
     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON =
@@ -137,6 +144,36 @@
     public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
             AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15
 
+    /**
+     * Action ID to trigger the dpad up button
+     */
+    private static final int SYSTEM_ACTION_ID_DPAD_UP =
+            AccessibilityService.GLOBAL_ACTION_DPAD_UP; // 16
+
+    /**
+     * Action ID to trigger the dpad down button
+     */
+    private static final int SYSTEM_ACTION_ID_DPAD_DOWN =
+            AccessibilityService.GLOBAL_ACTION_DPAD_DOWN; // 17
+
+    /**
+     * Action ID to trigger the dpad left button
+     */
+    private static final int SYSTEM_ACTION_ID_DPAD_LEFT =
+            AccessibilityService.GLOBAL_ACTION_DPAD_LEFT; // 18
+
+    /**
+     * Action ID to trigger the dpad right button
+     */
+    private static final int SYSTEM_ACTION_ID_DPAD_RIGHT =
+            AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT; // 19
+
+    /**
+     * Action ID to trigger dpad center keyevent
+     */
+    private static final int SYSTEM_ACTION_ID_DPAD_CENTER =
+            AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20
+
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     private final SystemActionsBroadcastReceiver mReceiver;
@@ -222,10 +259,34 @@
                 R.string.accessibility_system_action_screenshot_label,
                 SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT);
 
+        RemoteAction actionHeadsetHook = createRemoteAction(
+                R.string.accessibility_system_action_headset_hook_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK);
+
         RemoteAction actionAccessibilityShortcut = createRemoteAction(
                 R.string.accessibility_system_action_hardware_a11y_shortcut_label,
                 SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
 
+        RemoteAction actionDpadUp = createRemoteAction(
+                R.string.accessibility_system_action_dpad_up_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP);
+
+        RemoteAction actionDpadDown = createRemoteAction(
+                R.string.accessibility_system_action_dpad_down_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN);
+
+        RemoteAction actionDpadLeft = createRemoteAction(
+                R.string.accessibility_system_action_dpad_left_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT);
+
+        RemoteAction actionDpadRight = createRemoteAction(
+                R.string.accessibility_system_action_dpad_right_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT);
+
+        RemoteAction actionDpadCenter = createRemoteAction(
+                R.string.accessibility_system_action_dpad_center_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER);
+
         mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
         mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
         mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
@@ -234,8 +295,14 @@
         mA11yManager.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
         mA11yManager.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
         mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
+        mA11yManager.registerSystemAction(actionHeadsetHook, SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK);
         mA11yManager.registerSystemAction(
                 actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT);
+        mA11yManager.registerSystemAction(actionDpadUp, SYSTEM_ACTION_ID_DPAD_UP);
+        mA11yManager.registerSystemAction(actionDpadDown, SYSTEM_ACTION_ID_DPAD_DOWN);
+        mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT);
+        mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT);
+        mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER);
         registerOrUnregisterDismissNotificationShadeAction();
     }
 
@@ -305,6 +372,10 @@
                 labelId = R.string.accessibility_system_action_screenshot_label;
                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT;
                 break;
+            case SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK:
+                labelId = R.string.accessibility_system_action_headset_hook_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK;
+                break;
             case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON:
                 labelId = R.string.accessibility_system_action_on_screen_a11y_shortcut_label;
                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON;
@@ -323,6 +394,26 @@
                 intent = SystemActionsBroadcastReceiver
                         .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE;
                 break;
+            case SYSTEM_ACTION_ID_DPAD_UP:
+                labelId = R.string.accessibility_system_action_dpad_up_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP;
+                break;
+            case SYSTEM_ACTION_ID_DPAD_DOWN:
+                labelId = R.string.accessibility_system_action_dpad_down_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN;
+                break;
+            case SYSTEM_ACTION_ID_DPAD_LEFT:
+                labelId = R.string.accessibility_system_action_dpad_left_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT;
+                break;
+            case SYSTEM_ACTION_ID_DPAD_RIGHT:
+                labelId = R.string.accessibility_system_action_dpad_right_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT;
+                break;
+            case SYSTEM_ACTION_ID_DPAD_CENTER:
+                labelId = R.string.accessibility_system_action_dpad_center_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER;
+                break;
             default:
                 return;
         }
@@ -411,6 +502,10 @@
                 SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
     }
 
+    private void handleHeadsetHook() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
+    }
+
     private void handleAccessibilityButton() {
         AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
                 Display.DEFAULT_DISPLAY);
@@ -434,6 +529,26 @@
                         CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
     }
 
+    private void handleDpadUp() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP);
+    }
+
+    private void handleDpadDown() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN);
+    }
+
+    private void handleDpadLeft() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
+    }
+
+    private void handleDpadRight() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
+    }
+
+    private void handleDpadCenter() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
+    }
+
     private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
         private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
         private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -443,6 +558,7 @@
         private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG";
         private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN";
         private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT";
+        private static final String INTENT_ACTION_HEADSET_HOOK = "SYSTEM_ACTION_HEADSET_HOOK";
         private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON =
                 "SYSTEM_ACTION_ACCESSIBILITY_BUTTON";
         private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER =
@@ -451,6 +567,11 @@
                 "SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT";
         private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
                 "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE";
+        private static final String INTENT_ACTION_DPAD_UP = "SYSTEM_ACTION_DPAD_UP";
+        private static final String INTENT_ACTION_DPAD_DOWN = "SYSTEM_ACTION_DPAD_DOWN";
+        private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT";
+        private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT";
+        private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER";
 
         private PendingIntent createPendingIntent(Context context, String intentAction) {
             switch (intentAction) {
@@ -462,10 +583,16 @@
                 case INTENT_ACTION_POWER_DIALOG:
                 case INTENT_ACTION_LOCK_SCREEN:
                 case INTENT_ACTION_TAKE_SCREENSHOT:
+                case INTENT_ACTION_HEADSET_HOOK:
                 case INTENT_ACTION_ACCESSIBILITY_BUTTON:
                 case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER:
                 case INTENT_ACTION_ACCESSIBILITY_SHORTCUT:
-                case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
+                case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
+                case INTENT_ACTION_DPAD_UP:
+                case INTENT_ACTION_DPAD_DOWN:
+                case INTENT_ACTION_DPAD_LEFT:
+                case INTENT_ACTION_DPAD_RIGHT:
+                case INTENT_ACTION_DPAD_CENTER: {
                     Intent intent = new Intent(intentAction);
                     intent.setPackage(context.getPackageName());
                     return PendingIntent.getBroadcast(context, 0, intent,
@@ -487,10 +614,16 @@
             intentFilter.addAction(INTENT_ACTION_POWER_DIALOG);
             intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN);
             intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT);
+            intentFilter.addAction(INTENT_ACTION_HEADSET_HOOK);
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
             intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+            intentFilter.addAction(INTENT_ACTION_DPAD_UP);
+            intentFilter.addAction(INTENT_ACTION_DPAD_DOWN);
+            intentFilter.addAction(INTENT_ACTION_DPAD_LEFT);
+            intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT);
+            intentFilter.addAction(INTENT_ACTION_DPAD_CENTER);
             return intentFilter;
         }
 
@@ -530,6 +663,10 @@
                     handleTakeScreenshot();
                     break;
                 }
+                case INTENT_ACTION_HEADSET_HOOK: {
+                    handleHeadsetHook();
+                    break;
+                }
                 case INTENT_ACTION_ACCESSIBILITY_BUTTON: {
                     handleAccessibilityButton();
                     break;
@@ -546,6 +683,26 @@
                     handleAccessibilityDismissNotificationShade();
                     break;
                 }
+                case INTENT_ACTION_DPAD_UP: {
+                    handleDpadUp();
+                    break;
+                }
+                case INTENT_ACTION_DPAD_DOWN: {
+                    handleDpadDown();
+                    break;
+                }
+                case INTENT_ACTION_DPAD_LEFT: {
+                    handleDpadLeft();
+                    break;
+                }
+                case INTENT_ACTION_DPAD_RIGHT: {
+                    handleDpadRight();
+                    break;
+                }
+                case INTENT_ACTION_DPAD_CENTER: {
+                    handleDpadCenter();
+                    break;
+                }
                 default:
                     break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 12759f48..8b549b4 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -74,7 +74,7 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.systemui.R;
 import com.android.systemui.screenshot.FloatingWindowUtil;
-import com.android.systemui.screenshot.ScreenshotActionChip;
+import com.android.systemui.screenshot.OverlayActionChip;
 import com.android.systemui.screenshot.TimeoutHandler;
 
 import java.io.IOException;
@@ -106,12 +106,12 @@
     private final DraggableConstraintLayout mView;
     private final ImageView mImagePreview;
     private final TextView mTextPreview;
-    private final ScreenshotActionChip mEditChip;
-    private final ScreenshotActionChip mRemoteCopyChip;
+    private final OverlayActionChip mEditChip;
+    private final OverlayActionChip mRemoteCopyChip;
     private final View mActionContainerBackground;
     private final View mDismissButton;
     private final LinearLayout mActionContainer;
-    private final ArrayList<ScreenshotActionChip> mActionChips = new ArrayList<>();
+    private final ArrayList<OverlayActionChip> mActionChips = new ArrayList<>();
 
     private Runnable mOnSessionCompleteListener;
 
@@ -251,7 +251,7 @@
             for (RemoteAction action : actions) {
                 Intent targetIntent = action.getActionIntent().getIntent();
                 if (!TextUtils.equals(source, targetIntent.getComponent().getPackageName())) {
-                    ScreenshotActionChip chip = constructActionChip(action);
+                    OverlayActionChip chip = constructActionChip(action);
                     mActionContainer.addView(chip);
                     mActionChips.add(chip);
                 }
@@ -259,9 +259,9 @@
         });
     }
 
-    private ScreenshotActionChip constructActionChip(RemoteAction action) {
-        ScreenshotActionChip chip = (ScreenshotActionChip) LayoutInflater.from(mContext).inflate(
-                R.layout.screenshot_action_chip, mActionContainer, false);
+    private OverlayActionChip constructActionChip(RemoteAction action) {
+        OverlayActionChip chip = (OverlayActionChip) LayoutInflater.from(mContext).inflate(
+                R.layout.overlay_action_chip, mActionContainer, false);
         chip.setText(action.getTitle());
         chip.setIcon(action.getIcon(), false);
         chip.setPendingIntent(action.getActionIntent(), this::animateOut);
@@ -341,7 +341,7 @@
         mEditChip.setAlpha(1f);
         ContentResolver resolver = mContext.getContentResolver();
         try {
-            int size = mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale);
+            int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
             // The width of the view is capped, height maintains aspect ratio, so allow it to be
             // taller if needed.
             Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
@@ -365,7 +365,7 @@
     }
 
     private void animateOut() {
-        getExitAnimation().start();
+        mView.dismiss();
     }
 
     private ValueAnimator getEnterAnimation() {
@@ -401,28 +401,6 @@
         return anim;
     }
 
-    private ValueAnimator getExitAnimation() {
-        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-
-        anim.addUpdateListener(animation -> {
-            mView.setAlpha(1 - animation.getAnimatedFraction());
-            final View actionBackground = requireNonNull(
-                    mView.findViewById(R.id.actions_container_background));
-            mView.setTranslationX(
-                    -animation.getAnimatedFraction() * actionBackground.getWidth() / 2);
-        });
-
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                hideImmediate();
-            }
-        });
-
-        return anim;
-    }
-
     private void hideImmediate() {
         // Note this may be called multiple times if multiple dismissal events happen at the same
         // time.
@@ -453,7 +431,7 @@
     }
 
     private void resetActionChips() {
-        for (ScreenshotActionChip chip : mActionChips) {
+        for (OverlayActionChip chip : mActionChips) {
             mActionContainer.removeView(chip);
         }
         mActionChips.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
index 6a4be6e..8843462 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
@@ -98,10 +98,23 @@
         return mSwipeDetector.onTouchEvent(ev);
     }
 
+    /**
+     * Dismiss the view, with animation controlled by SwipeDismissHandler
+     */
+    public void dismiss() {
+        mSwipeDismissHandler.dismiss();
+    }
+
+    /**
+     * Set the callback to be run after view is dismissed
+     */
     public void setOnDismissCallback(Runnable callback) {
         mOnDismiss = callback;
     }
 
+    /**
+     * Set the callback to be run when the view is interacted with (e.g. tapped)
+     */
     public void setOnInteractionCallback(Runnable callback) {
         mOnInteraction = callback;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c894b70..357a68f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -107,6 +107,8 @@
     public static final ResourceBooleanFlag QS_USER_DETAIL_SHORTCUT =
             new ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut);
 
+    public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, false);
+
     /***************************************/
     // 600- status bar
     public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
index d1a103e..de67ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
@@ -40,6 +40,7 @@
     private boolean mIsDropDownMode;
     private int mMenuVerticalPadding = 0;
     private int mGlobalActionsSidePadding = 0;
+    private int mMaximumWidthThresholdDp = 800;
     private ListAdapter mAdapter;
     private AdapterView.OnItemLongClickListener mOnItemLongClickListener;
 
@@ -92,6 +93,8 @@
 
             // width should be between [.5, .9] of screen
             int parentWidth = res.getSystem().getDisplayMetrics().widthPixels;
+            float parentDensity = res.getSystem().getDisplayMetrics().density;
+            float parentWidthDp = parentWidth / parentDensity;
             int widthSpec = MeasureSpec.makeMeasureSpec(
                     (int) (parentWidth * 0.9), MeasureSpec.AT_MOST);
             int maxWidth = 0;
@@ -101,9 +104,12 @@
                 int w = child.getMeasuredWidth();
                 maxWidth = Math.max(w, maxWidth);
             }
-            int width = Math.max(maxWidth, (int) (parentWidth * 0.5));
-            listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding);
 
+            int width = maxWidth;
+            if (parentWidthDp < mMaximumWidthThresholdDp) {
+                width = Math.max(maxWidth, (int) (parentWidth * 0.5));
+            }
+            listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding);
             setWidth(width);
             if (getAnchorView().getLayoutDirection() == LayoutDirection.LTR) {
                 setHorizontalOffset(getAnchorView().getWidth() - mGlobalActionsSidePadding - width);
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index b15807c..6d589aa 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -176,14 +176,9 @@
             buffer.removeFirst()
         }
         buffer.add(message as LogMessageImpl)
-        if (systrace) {
-            val messageStr = message.printer(message)
-            Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "$name - $messageStr")
-        }
-        if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
-                logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
-            echo(message)
-        }
+        val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) ||
+                logcatEchoTracker.isTagLoggable(message.tag, message.level)
+        echo(message, toLogcat = includeInLogcat, toSystrace = systrace)
     }
 
     /** Converts the entire buffer to a newline-delimited string */
@@ -232,8 +227,24 @@
         pw.println(message.printer(message))
     }
 
-    private fun echo(message: LogMessage) {
-        val strMessage = message.printer(message)
+    private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
+        if (toLogcat || toSystrace) {
+            val strMessage = message.printer(message)
+            if (toSystrace) {
+                echoToSystrace(message, strMessage)
+            }
+            if (toLogcat) {
+                echoToLogcat(message, strMessage)
+            }
+        }
+    }
+
+    private fun echoToSystrace(message: LogMessage, strMessage: String) {
+        Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events",
+            "$name - ${message.level.shortString} ${message.tag}: $strMessage")
+    }
+
+    private fun echoToLogcat(message: LogMessage, strMessage: String) {
         when (message.level) {
             LogLevel.VERBOSE -> Log.v(message.tag, strMessage)
             LogLevel.DEBUG -> Log.d(message.tag, strMessage)
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 44727f2..48f4826 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -159,8 +159,7 @@
     }
 
     fun refreshMediaPosition() {
-        val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD ||
-                statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER)
+        val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD)
         // mediaHost.visible required for proper animations handling
         visible = mediaHost.visible &&
                 !bypassController.bypassEnabled &&
@@ -196,4 +195,4 @@
             visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index c404f7a..f893f36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -411,7 +411,6 @@
 
     // Returns true if new player is added
     private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
-        val dataCopy = data.copy(backgroundColor = bgColor)
         MediaPlayerData.moveIfExists(oldKey, key)
         val existingPlayer = MediaPlayerData.getMediaPlayer(key)
         val curVisibleMediaKey = MediaPlayerData.playerKeys()
@@ -431,14 +430,14 @@
             val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT)
             newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
-            newPlayer.bindPlayer(dataCopy, key)
+            newPlayer.bindPlayer(data, key)
             newPlayer.setListening(currentlyExpanded)
-            MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer, systemClock)
+            MediaPlayerData.addMediaPlayer(key, data, newPlayer, systemClock)
             updatePlayerToState(newPlayer, noAnimation = true)
             reorderAllPlayers(curVisibleMediaKey)
         } else {
-            existingPlayer.bindPlayer(dataCopy, key)
-            MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer, systemClock)
+            existingPlayer.bindPlayer(data, key)
+            MediaPlayerData.addMediaPlayer(key, data, existingPlayer, systemClock)
             if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) {
                 reorderAllPlayers(curVisibleMediaKey)
             } else {
@@ -543,7 +542,11 @@
     }
 
     private fun getForegroundColor(): Int {
-        return context.getColor(android.R.color.system_accent2_900)
+        return if (mediaFlags.useMediaSessionLayout()) {
+            context.getColor(android.R.color.system_neutral2_200)
+        } else {
+            context.getColor(android.R.color.system_accent2_900)
+        }
     }
 
     private fun updatePageIndicator() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 69a7ec3..b3e6682 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
 
 import android.app.PendingIntent;
+import android.app.WallpaperColors;
 import android.app.smartspace.SmartspaceAction;
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +41,7 @@
 import android.view.ViewGroup;
 import android.widget.ImageButton;
 import android.widget.ImageView;
+import android.widget.SeekBar;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -54,6 +56,7 @@
 import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -405,7 +408,7 @@
         seamlessView.setContentDescription(deviceString);
 
         // Dismiss
-        mMediaViewHolder.getDismissLabel().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
+        mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
         mMediaViewHolder.getDismiss().setEnabled(isDismissible);
         mMediaViewHolder.getDismiss().setOnClickListener(v -> {
             if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
@@ -438,11 +441,10 @@
         ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
 
         // Album art
-        PlayerViewHolder playerHolder = (PlayerViewHolder) mMediaViewHolder;
-        ImageView albumView = playerHolder.getAlbumView();
+        ImageView albumView = mMediaViewHolder.getAlbumView();
         boolean hasArtwork = data.getArtwork() != null;
         if (hasArtwork) {
-            Drawable artwork = scaleDrawable(data.getArtwork());
+            Drawable artwork = getScaledThumbnail(data.getArtwork());
             albumView.setPadding(0, 0, 0, 0);
             albumView.setImageDrawable(artwork);
         } else {
@@ -548,6 +550,19 @@
 
     /** Bind elements specific to PlayerSessionViewHolder */
     private void bindSessionPlayer(@NonNull MediaData data, String key) {
+        // Default colors
+        int surfaceColor = mBackgroundColor;
+        int accentPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+        int textPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+        int textPrimaryInverse = com.android.settingslib.Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.textColorPrimaryInverse).getDefaultColor();
+        int textSecondary = com.android.settingslib.Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.textColorSecondary).getDefaultColor();
+        int textTertiary = com.android.settingslib.Utils.getColorAttr(mContext,
+                com.android.internal.R.attr.textColorTertiary).getDefaultColor();
+
         // App icon - use launcher icon
         ImageView appIconView = mMediaViewHolder.getAppIcon();
         appIconView.clearColorFilter();
@@ -567,26 +582,106 @@
             appIconView.setColorFilter(color);
         }
 
+        // Album art
+        ColorScheme colorScheme = null;
+        ImageView albumView = mMediaViewHolder.getAlbumView();
+        boolean hasArtwork = data.getArtwork() != null;
+        if (hasArtwork) {
+            colorScheme = new ColorScheme(WallpaperColors.fromBitmap(data.getArtwork().getBitmap()),
+                        true);
+
+            // Scale artwork to fit background
+            int width = mMediaViewHolder.getPlayer().getWidth();
+            int height = mMediaViewHolder.getPlayer().getHeight();
+            Drawable artwork = getScaledBackground(data.getArtwork(), width, height);
+            albumView.setPadding(0, 0, 0, 0);
+            albumView.setImageDrawable(artwork);
+            albumView.setClipToOutline(true);
+        } else {
+            // If there's no artwork, use colors from the app icon
+            try {
+                Drawable icon = mContext.getPackageManager().getApplicationIcon(
+                        data.getPackageName());
+                colorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
+            }
+        }
+
+        // Get colors for player
+        if (colorScheme != null) {
+            surfaceColor = colorScheme.getAccent2().get(9); // A2-800
+            accentPrimary = colorScheme.getAccent1().get(2); // A1-100
+            textPrimary = colorScheme.getNeutral1().get(1); // N1-50
+            textPrimaryInverse = colorScheme.getNeutral1().get(10); // N1-900
+            textSecondary = colorScheme.getNeutral2().get(3); // N2-200
+            textTertiary = colorScheme.getNeutral2().get(5); // N2-400
+        }
+
+        ColorStateList bgColorList = ColorStateList.valueOf(surfaceColor);
+        ColorStateList accentColorList = ColorStateList.valueOf(accentPrimary);
+        ColorStateList textColorList = ColorStateList.valueOf(textPrimary);
+
+        // Gradient and background (visible when there is no art)
+        albumView.setForegroundTintList(ColorStateList.valueOf(surfaceColor));
+        albumView.setBackgroundTintList(
+                ColorStateList.valueOf(surfaceColor));
+        mMediaViewHolder.getPlayer().setBackgroundTintList(bgColorList);
+
+        // Metadata text
+        mMediaViewHolder.getTitleText().setTextColor(textPrimary);
+        mMediaViewHolder.getArtistText().setTextColor(textSecondary);
+
+        // Seekbar
+        SeekBar seekbar = mMediaViewHolder.getSeekBar();
+        seekbar.getThumb().setTintList(textColorList);
+        seekbar.setProgressTintList(textColorList);
+        seekbar.setProgressBackgroundTintList(ColorStateList.valueOf(textTertiary));
+
+        // Output switcher
+        View seamlessView = mMediaViewHolder.getSeamlessButton();
+        seamlessView.setBackgroundTintList(accentColorList);
+        ImageView seamlessIconView = mMediaViewHolder.getSeamlessIcon();
+        seamlessIconView.setImageTintList(bgColorList);
+        TextView seamlessText = mMediaViewHolder.getSeamlessText();
+        seamlessText.setTextColor(surfaceColor);
+
         // Media action buttons
         MediaButton semanticActions = data.getSemanticActions();
         if (semanticActions != null) {
             PlayerSessionViewHolder sessionHolder = (PlayerSessionViewHolder) mMediaViewHolder;
-            setSemanticButton(sessionHolder.getActionPlayPause(),
-                    semanticActions.getPlayOrPause());
-            setSemanticButton(sessionHolder.getActionNext(),
-                    semanticActions.getNextOrCustom());
-            setSemanticButton(sessionHolder.getActionPrev(),
-                    semanticActions.getPrevOrCustom());
-            setSemanticButton(sessionHolder.getActionStart(),
-                    semanticActions.getStartCustom());
-            setSemanticButton(sessionHolder.getActionEnd(),
-                    semanticActions.getEndCustom());
+
+            // Play/pause button has a background
+            sessionHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
+            setSemanticButton(sessionHolder.getActionPlayPause(), semanticActions.getPlayOrPause(),
+                    ColorStateList.valueOf(textPrimaryInverse));
+
+            setSemanticButton(sessionHolder.getActionNext(), semanticActions.getNextOrCustom(),
+                    textColorList);
+            setSemanticButton(sessionHolder.getActionPrev(), semanticActions.getPrevOrCustom(),
+                    textColorList);
+            setSemanticButton(sessionHolder.getActionStart(), semanticActions.getStartCustom(),
+                    textColorList);
+            setSemanticButton(sessionHolder.getActionEnd(), semanticActions.getEndCustom(),
+                    textColorList);
         } else {
             Log.w(TAG, "Using semantic player, but did not get buttons");
         }
+
+        // Long press buttons
+        mMediaViewHolder.getLongPressText().setTextColor(textColorList);
+        mMediaViewHolder.getSettingsText().setTextColor(textColorList);
+        mMediaViewHolder.getSettingsText().setBackgroundTintList(accentColorList);
+        mMediaViewHolder.getCancelText().setTextColor(textColorList);
+        mMediaViewHolder.getCancelText().setBackgroundTintList(accentColorList);
+        mMediaViewHolder.getDismissText().setTextColor(textColorList);
+        mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList);
+
     }
 
-    private void setSemanticButton(final ImageButton button, MediaAction mediaAction) {
+    private void setSemanticButton(final ImageButton button, MediaAction mediaAction,
+            ColorStateList fgColor) {
+        button.setImageTintList(fgColor);
         if (mediaAction != null) {
             button.setImageIcon(mediaAction.getIcon());
             button.setContentDescription(mediaAction.getContentDescription());
@@ -844,8 +939,11 @@
         mMediaViewController.openGuts();
     }
 
+    /**
+     * Scale drawable to fit into the square album art thumbnail
+     */
     @UiThread
-    private Drawable scaleDrawable(Icon icon) {
+    private Drawable getScaledThumbnail(Icon icon) {
         if (icon == null) {
             return null;
         }
@@ -870,6 +968,25 @@
     }
 
     /**
+     * Scale artwork to fill the background of the panel
+     */
+    @UiThread
+    private Drawable getScaledBackground(Icon icon, int width, int height) {
+        if (icon == null) {
+            return null;
+        }
+        Drawable drawable = icon.loadDrawable(mContext);
+        Rect bounds = new Rect(0, 0, width, height);
+        if (bounds.width() > width || bounds.height() > height) {
+            float offsetX = (bounds.width() - width) / 2.0f;
+            float offsetY = (bounds.height() - height) / 2.0f;
+            bounds.offset((int) -offsetX, (int) -offsetY);
+        }
+        drawable.setBounds(bounds);
+        return drawable;
+    }
+
+    /**
      * Get the current media controller
      *
      * @return the controller
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index d926e7d..0223c60 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -30,9 +30,7 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
-import android.graphics.Canvas
 import android.graphics.ImageDecoder
-import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.media.MediaDescription
 import android.media.MediaMetadata
@@ -562,7 +560,7 @@
         val mediaController = mediaControllerFactory.create(token)
         val metadata = mediaController.metadata
 
-        // Foreground and Background colors computed from album art
+        // Album art
         val notif: Notification = sbn.notification
         var artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
         if (artworkBitmap == null) {
@@ -576,24 +574,6 @@
         } else {
             Icon.createWithBitmap(artworkBitmap)
         }
-        if (artWorkIcon != null) {
-            // If we have art, get colors from that
-            if (artworkBitmap == null) {
-                if (artWorkIcon.type == Icon.TYPE_BITMAP ||
-                        artWorkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP) {
-                    artworkBitmap = artWorkIcon.bitmap
-                } else {
-                    val drawable: Drawable = artWorkIcon.loadDrawable(context)
-                    artworkBitmap = Bitmap.createBitmap(
-                            drawable.intrinsicWidth,
-                            drawable.intrinsicHeight,
-                            Bitmap.Config.ARGB_8888)
-                    val canvas = Canvas(artworkBitmap)
-                    drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
-                    drawable.draw(canvas)
-                }
-            }
-        }
 
         // App name
         val builder = Notification.Builder.recoverBuilder(context, notif)
@@ -787,30 +767,28 @@
         return when (action) {
             PlaybackState.ACTION_PLAY -> {
                 MediaAction(
-                    Icon.createWithResource(context, com.android.internal.R.drawable.ic_media_play),
+                    Icon.createWithResource(context, R.drawable.ic_media_play),
                     { controller.transportControls.play() },
                     context.getString(R.string.controls_media_button_play)
                 )
             }
             PlaybackState.ACTION_PAUSE -> {
                 MediaAction(
-                    Icon.createWithResource(context,
-                        com.android.internal.R.drawable.ic_media_pause),
+                    Icon.createWithResource(context, R.drawable.ic_media_pause),
                     { controller.transportControls.pause() },
                     context.getString(R.string.controls_media_button_pause)
                 )
             }
             PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
                 MediaAction(
-                    Icon.createWithResource(context,
-                        com.android.internal.R.drawable.ic_media_previous),
+                    Icon.createWithResource(context, R.drawable.ic_media_prev),
                     { controller.transportControls.skipToPrevious() },
                     context.getString(R.string.controls_media_button_prev)
                 )
             }
             PlaybackState.ACTION_SKIP_TO_NEXT -> {
                 MediaAction(
-                    Icon.createWithResource(context, com.android.internal.R.drawable.ic_media_next),
+                    Icon.createWithResource(context, R.drawable.ic_media_next),
                     { controller.transportControls.skipToNext() },
                     context.getString(R.string.controls_media_button_next)
                 )
@@ -900,7 +878,7 @@
 
     private fun getResumeMediaAction(action: Runnable): MediaAction {
         return MediaAction(
-            Icon.createWithResource(context, R.drawable.lb_ic_play).setTint(themeText),
+            Icon.createWithResource(context, R.drawable.ic_media_play).setTint(themeText),
             action,
             context.getString(R.string.controls_media_resume)
         )
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 64ebe56..6145f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -567,8 +567,7 @@
                 previousLocation = this.desiredLocation
             } else if (forceStateUpdate) {
                 val onLockscreen = (!bypassController.bypassEnabled &&
-                        (statusbarState == StatusBarState.KEYGUARD ||
-                            statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
+                        (statusbarState == StatusBarState.KEYGUARD))
                 if (desiredLocation == LOCATION_QS && previousLocation == LOCATION_LOCKSCREEN &&
                         !onLockscreen) {
                     // If media active state changed and the device is now unlocked, update the
@@ -955,8 +954,7 @@
             return desiredLocation
         }
         val onLockscreen = (!bypassController.bypassEnabled &&
-            (statusbarState == StatusBarState.KEYGUARD ||
-                statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
+            (statusbarState == StatusBarState.KEYGUARD))
         val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
         val location = when {
             dreamOverlayActive -> LOCATION_DREAM_OVERLAY
@@ -1087,4 +1085,4 @@
 @IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS,
     MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN])
 @Retention(AnnotationRetention.SOURCE)
-annotation class MediaLocation
\ No newline at end of file
+annotation class MediaLocation
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
index c333b50..e57b247 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
@@ -35,6 +35,7 @@
     val player = itemView as TransitionLayout
 
     // Player information
+    val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
     val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
     val titleText = itemView.requireViewById<TextView>(R.id.header_title)
     val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
@@ -53,8 +54,9 @@
     // Settings screen
     val longPressText = itemView.requireViewById<TextView>(R.id.remove_text)
     val cancel = itemView.requireViewById<View>(R.id.cancel)
+    val cancelText = itemView.requireViewById<TextView>(R.id.cancel_text)
     val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
-    val dismissLabel = dismiss.getChildAt(0)
+    val dismissText = itemView.requireViewById<TextView>(R.id.dismiss_text)
     val settings = itemView.requireViewById<View>(R.id.settings)
     val settingsText = itemView.requireViewById<TextView>(R.id.settings_text)
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index a1faa40..20b2d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -20,7 +20,6 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageButton
-import android.widget.ImageView
 import android.widget.TextView
 import com.android.systemui.R
 
@@ -29,9 +28,6 @@
  */
 class PlayerViewHolder private constructor(itemView: View) : MediaViewHolder(itemView) {
 
-    // Player information
-    val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
-
     // Seek bar
     val progressTimes = itemView.requireViewById<ViewGroup>(R.id.notification_media_progress_time)
     override val elapsedTimeView = itemView.requireViewById<TextView>(R.id.media_elapsed_time)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 2bc910e..8684509 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -26,11 +26,12 @@
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.media.MediaHostStatesManager;
 import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
+import com.android.systemui.media.nearby.NearbyMediaDevicesService;
 import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
 
 import java.util.Optional;
@@ -99,11 +100,12 @@
     static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
             MediaTttFlags mediaTttFlags,
             Context context,
-            WindowManager windowManager) {
+            WindowManager windowManager,
+            CommandQueue commandQueue) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaTttChipControllerSender(context, windowManager));
+        return Optional.of(new MediaTttChipControllerSender(context, windowManager, commandQueue));
     }
 
     /** */
@@ -137,9 +139,9 @@
                         mediaTttChipControllerReceiver));
     }
 
-    /** Inject into MediaTttSenderService. */
+    /** Inject into NearbyMediaDevicesService. */
     @Binds
     @IntoMap
-    @ClassKey(MediaTttSenderService.class)
-    Service bindMediaTttSenderService(MediaTttSenderService service);
+    @ClassKey(NearbyMediaDevicesService.class)
+    Service bindMediaNearbyDevicesService(NearbyMediaDevicesService service);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt
deleted file mode 100644
index 0453fdb..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.nearby
-
-import com.android.systemui.dagger.SysUISingleton
-
-/**
- * A manager that returns information about devices that are nearby and can receive media transfers.
- */
-@SysUISingleton
-class MediaNearbyDevicesManager {
-
-    /** Returns a list containing the current nearby devices. */
-    fun getCurrentNearbyDevices(): List<NearbyDevice> {
-        // TODO(b/216313420): Implement this function.
-        return emptyList()
-    }
-
-    /**
-     * Registers [callback] to be notified each time a device's range changes or when a new device
-     * comes within range.
-     */
-    fun registerNearbyDevicesCallback(
-        callback: (device: NearbyDevice) -> Unit
-    ) {
-        // TODO(b/216313420): Implement this function.
-    }
-
-    /**
-     * Un-registers [callback]. See [registerNearbyDevicesCallback].
-     */
-    fun unregisterNearbyDevicesCallback(
-        callback: (device: NearbyDevice) -> Unit
-    ) {
-        // TODO(b/216313420): Implement this function.
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt
new file mode 100644
index 0000000..eaf2bd9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.nearby
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.media.INearbyMediaDevicesProvider
+import com.android.systemui.shared.media.INearbyMediaDevicesService
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
+import com.android.systemui.shared.media.NearbyDevice
+import javax.inject.Inject
+
+/**
+ * A service that acts as a bridge between (1) external clients that have data on nearby devices
+ * that are able to play media and (2) internal clients (like media Output Switcher) that need data
+ * on these nearby devices.
+ *
+ * TODO(b/216313420): Add logging to this class.
+ */
+@SysUISingleton
+class NearbyMediaDevicesService @Inject constructor() : Service() {
+
+    private var provider: INearbyMediaDevicesProvider? = null
+
+    private val binder: IBinder = object : INearbyMediaDevicesService.Stub() {
+        override fun registerProvider(newProvider: INearbyMediaDevicesProvider) {
+            provider = newProvider
+            newProvider.asBinder().linkToDeath(
+                {
+                    // We might've gotten a new provider before the old provider died, so we only
+                    // need to clear our provider if the most recent provider died.
+                    if (provider == newProvider) {
+                        provider = null
+                    }
+                },
+                /* flags= */ 0
+            )
+        }
+    }
+
+    override fun onBind(intent: Intent?): IBinder = binder
+
+    /** Returns a list containing the current nearby devices. */
+    fun getCurrentNearbyDevices(): List<NearbyDevice> {
+        val currentProvider = provider ?: return emptyList()
+        return currentProvider.currentNearbyDevices
+    }
+
+    /**
+     * Registers [callback] to be notified each time a device's range changes or when a new device
+     * comes within range.
+     */
+    fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
+        val currentProvider = provider ?: return
+        currentProvider.registerNearbyDevicesCallback(callback)
+    }
+
+    /**
+     * Un-registers [callback]. See [registerNearbyDevicesCallback].
+     */
+    fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
+        val currentProvider = provider ?: return
+        currentProvider.unregisterNearbyDevicesCallback(callback)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt
deleted file mode 100644
index 3c890bc..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.nearby
-
-import androidx.annotation.IntDef
-import kotlin.annotation.AnnotationRetention
-
-@IntDef(
-        RangeZone.RANGE_UNKNOWN,
-        RangeZone.RANGE_FAR,
-        RangeZone.RANGE_LONG,
-        RangeZone.RANGE_CLOSE,
-        RangeZone.RANGE_WITHIN_REACH
-)
-@Retention(AnnotationRetention.SOURCE)
-/** The various range zones a device can be in, in relation to the current device. */
-annotation class RangeZone {
-    companion object {
-        /** Unknown distance range. */
-        const val RANGE_UNKNOWN = 0
-        /** Distance is very far away from the peer device. */
-        const val RANGE_FAR = 1
-        /** Distance is relatively long from the peer device, typically a few meters. */
-        const val RANGE_LONG = 2
-        /** Distance is close to the peer device, typically with one or two meter. */
-        const val RANGE_CLOSE = 3
-        /** Distance is very close to the peer device, typically within one meter or less. */
-        const val RANGE_WITHIN_REACH = 4
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 3720851..bbcbfba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,21 +16,16 @@
 
 package com.android.systemui.media.taptotransfer
 
-import android.content.ComponentName
+import android.app.StatusBarManager
 import android.content.Context
-import android.content.Intent
-import android.content.ServiceConnection
 import android.graphics.Color
 import android.graphics.drawable.Icon
 import android.media.MediaRoute2Info
-import android.os.IBinder
-import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
 import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
 import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
 import com.android.systemui.media.taptotransfer.sender.TransferFailed
@@ -38,9 +33,6 @@
 import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
 import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
 import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderService
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import java.io.PrintWriter
@@ -56,10 +48,7 @@
     private val context: Context,
     private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
 ) {
-    private var senderService: IDeviceSenderService? = null
-    private val senderServiceConnection = SenderServiceConnection()
-
-    private val appIconDrawable =
+   private val appIconDrawable =
         Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
             it.setTint(Color.YELLOW)
         }
@@ -75,115 +64,24 @@
     /** All commands for the sender device. */
     inner class SenderCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
-            val otherDeviceName = args[0]
-            val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
-                .addFeature("feature")
-                .build()
-            val otherDeviceInfo = DeviceInfo(otherDeviceName)
+            val routeInfo = MediaRoute2Info.Builder("id", args[0])
+                    .addFeature("feature")
+                    .build()
 
-            when (args[1]) {
-                MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
-                    runOnService { senderService ->
-                        senderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
-                    }
-                }
-                MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> {
-                    runOnService { senderService ->
-                        senderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
-                    }
-                }
-                TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> {
-                    runOnService { senderService ->
-                        senderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
-                    }
-                }
-                TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> {
-                    runOnService { senderService ->
-                        senderService.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
-                    }
-                }
-                TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME -> {
-                    val undoCallback = object : IUndoTransferCallback.Stub() {
-                        override fun onUndoTriggered() {
-                            Log.i(TAG, "Undo transfer to receiver callback triggered")
-                            // The external services that implement this callback would kick off a
-                            // transfer back to this device, so mimic that here.
-                            runOnService { senderService ->
-                                senderService
-                                    .transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
-                            }
-                        }
-                    }
-                    runOnService { senderService ->
-                        senderService
-                            .transferToReceiverSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
-                    }
-                }
-                TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME -> {
-                    val undoCallback = object : IUndoTransferCallback.Stub() {
-                        override fun onUndoTriggered() {
-                            Log.i(TAG, "Undo transfer to this device callback triggered")
-                            // The external services that implement this callback would kick off a
-                            // transfer back to the receiver, so mimic that here.
-                            runOnService { senderService ->
-                                senderService
-                                    .transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
-                            }
-                        }
-                    }
-                    runOnService { senderService ->
-                        senderService
-                            .transferToThisDeviceSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
-                    }
-                }
-                TRANSFER_FAILED_COMMAND_NAME -> {
-                    runOnService { senderService ->
-                        senderService.transferFailed(mediaInfo, otherDeviceInfo)
-                    }
-                }
-                NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> {
-                    runOnService { senderService ->
-                        senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo)
-                        context.unbindService(senderServiceConnection)
-                    }
-                }
-                else -> {
-                    pw.println("Sender command must be one of " +
-                            "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
-                            "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
-                            "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
-                            "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " +
-                            "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " +
-                            "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " +
-                            "$TRANSFER_FAILED_COMMAND_NAME, " +
-                            NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
-                    )
-                }
-            }
+            val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
+                    as StatusBarManager
+            statusBarManager.updateMediaTapToTransferSenderDisplay(
+                    StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+                    routeInfo,
+                    /* undoExecutor= */ null,
+                    /* undoCallback= */ null
+            )
+            // TODO(b/216318437): Migrate the rest of the callbacks to StatusBarManager.
         }
 
         override fun help(pw: PrintWriter) {
             pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>")
         }
-
-        private fun runOnService(command: SenderServiceCommand) {
-            val currentService = senderService
-            if (currentService != null) {
-                command.run(currentService)
-            } else {
-                bindService(command)
-            }
-        }
-
-        private fun bindService(command: SenderServiceCommand) {
-            senderServiceConnection.pendingCommand = command
-            val binding = context.bindService(
-                Intent(context, MediaTttSenderService::class.java),
-                senderServiceConnection,
-                Context.BIND_AUTO_CREATE
-            )
-            Log.i(TAG, "Starting service binding? $binding")
-        }
     }
 
     /** A command to DISPLAY the media ttt chip on the RECEIVER device. */
@@ -207,29 +105,6 @@
             pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG")
         }
     }
-
-    /** A service connection for [IDeviceSenderService]. */
-    private inner class SenderServiceConnection : ServiceConnection {
-        // A command that should be run when the service gets connected.
-        var pendingCommand: SenderServiceCommand? = null
-
-        override fun onServiceConnected(className: ComponentName, service: IBinder) {
-            val newCallback = IDeviceSenderService.Stub.asInterface(service)
-            senderService = newCallback
-            pendingCommand?.run(newCallback)
-            pendingCommand = null
-        }
-
-        override fun onServiceDisconnected(className: ComponentName) {
-            senderService = null
-        }
-    }
-
-    /** An interface defining a command that should be run on the sender service. */
-    private fun interface SenderServiceCommand {
-        /** Runs the command on the provided [senderService]. */
-        fun run(senderService: IDeviceSenderService)
-    }
 }
 
 @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index c656df2..118a04c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -19,9 +19,9 @@
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.view.View
+import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.R
 import com.android.systemui.media.taptotransfer.common.MediaTttChipState
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
 
 /**
  * A class that stores all the information necessary to display the media tap-to-transfer chip on
@@ -130,7 +130,7 @@
     appIconDrawable: Drawable,
     appIconContentDescription: String,
     private val otherDeviceName: String,
-    val undoCallback: IUndoTransferCallback? = null
+    val undoCallback: IUndoMediaTransferCallback? = null
 ) : ChipStateSender(appIconDrawable, appIconContentDescription) {
     override fun getChipTextString(context: Context): String {
         return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
@@ -169,7 +169,7 @@
     appIconDrawable: Drawable,
     appIconContentDescription: String,
     private val otherDeviceName: String,
-    val undoCallback: IUndoTransferCallback? = null
+    val undoCallback: IUndoMediaTransferCallback? = null
 ) : ChipStateSender(appIconDrawable, appIconContentDescription) {
     override fun getChipTextString(context: Context): String {
         return context.getString(R.string.media_transfer_playing_this_device)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 453e3d6..c510e35 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -16,14 +16,20 @@
 
 package com.android.systemui.media.taptotransfer.sender
 
+import android.app.StatusBarManager
 import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.widget.TextView
+import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
+import com.android.systemui.statusbar.CommandQueue
 import javax.inject.Inject
 
 /**
@@ -34,9 +40,36 @@
 class MediaTttChipControllerSender @Inject constructor(
     context: Context,
     windowManager: WindowManager,
+    private val commandQueue: CommandQueue
 ) : MediaTttChipControllerCommon<ChipStateSender>(
     context, windowManager, R.layout.media_ttt_chip
 ) {
+    // TODO(b/216141276): Use app icon from media route info instead of this fake one.
+    private val fakeAppIconDrawable =
+        Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
+            it.setTint(Color.YELLOW)
+        }
+
+    private val commandQueueCallback = object : CommandQueue.Callbacks {
+        override fun updateMediaTapToTransferSenderDisplay(
+                @StatusBarManager.MediaTransferSenderState displayState: Int,
+                routeInfo: MediaRoute2Info,
+                undoCallback: IUndoMediaTransferCallback?
+        ) {
+            // TODO(b/216318437): Trigger displayChip with the right state based on displayState.
+            displayChip(
+                MoveCloserToStartCast(
+                    // TODO(b/217418566): This app icon content description is incorrect --
+                    //   routeInfo.name is the name of the device, not the name of the app.
+                    fakeAppIconDrawable, routeInfo.name.toString(), routeInfo.name.toString()
+                )
+            )
+        }
+    }
+
+    init {
+        commandQueue.addCallback(commandQueueCallback)
+    }
 
     /** Displays the chip view for the given state. */
     override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
deleted file mode 100644
index 717752e..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.taptotransfer.sender
-
-import android.app.Service
-import android.content.Context
-import android.content.Intent
-import android.graphics.Color
-import android.graphics.drawable.Icon
-import android.media.MediaRoute2Info
-import android.os.IBinder
-import com.android.systemui.R
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
-import com.android.systemui.shared.mediattt.IDeviceSenderService
-import javax.inject.Inject
-
-/**
- * Service that allows external handlers to trigger the media chip on the sender device.
- */
-class MediaTttSenderService @Inject constructor(
-    context: Context,
-    val controller: MediaTttChipControllerSender
-) : Service() {
-
-    // TODO(b/203800643): Add logging when callbacks trigger.
-    private val binder: IBinder = object : IDeviceSenderService.Stub() {
-        override fun closeToReceiverToStartCast(
-            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-        ) {
-            this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
-        }
-
-        override fun closeToReceiverToEndCast(
-            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-        ) {
-            this@MediaTttSenderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
-        }
-
-        override fun transferFailed(
-            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-        ) {
-            this@MediaTttSenderService.transferFailed(mediaInfo)
-        }
-
-        override fun transferToReceiverTriggered(
-            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-        ) {
-            this@MediaTttSenderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
-        }
-
-        override fun transferToThisDeviceTriggered(
-            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-        ) {
-            this@MediaTttSenderService.transferToThisDeviceTriggered(mediaInfo)
-        }
-
-        override fun transferToReceiverSucceeded(
-            mediaInfo: MediaRoute2Info,
-            otherDeviceInfo: DeviceInfo,
-            undoCallback: IUndoTransferCallback
-        ) {
-            this@MediaTttSenderService.transferToReceiverSucceeded(
-                mediaInfo, otherDeviceInfo, undoCallback
-            )
-        }
-
-        override fun transferToThisDeviceSucceeded(
-            mediaInfo: MediaRoute2Info,
-            otherDeviceInfo: DeviceInfo,
-            undoCallback: IUndoTransferCallback
-        ) {
-            this@MediaTttSenderService.transferToThisDeviceSucceeded(
-                mediaInfo, otherDeviceInfo, undoCallback
-            )
-        }
-
-        override fun noLongerCloseToReceiver(
-            mediaInfo: MediaRoute2Info,
-            otherDeviceInfo: DeviceInfo
-        ) {
-            this@MediaTttSenderService.noLongerCloseToReceiver()
-        }
-    }
-
-    // TODO(b/203800643): Use the app icon from the media info instead of a fake one.
-    private val fakeAppIconDrawable =
-        Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
-            it.setTint(Color.YELLOW)
-        }
-
-    override fun onBind(intent: Intent?): IBinder = binder
-
-    private fun closeToReceiverToStartCast(
-        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-    ) {
-        val chipState = MoveCloserToStartCast(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString(),
-            otherDeviceName = otherDeviceInfo.name
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
-        val chipState = MoveCloserToEndCast(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString(),
-            otherDeviceName = otherDeviceInfo.name
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun transferFailed(mediaInfo: MediaRoute2Info) {
-        val chipState = TransferFailed(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString()
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun transferToReceiverTriggered(
-        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
-    ) {
-        val chipState = TransferToReceiverTriggered(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString(),
-            otherDeviceName = otherDeviceInfo.name
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun transferToThisDeviceTriggered(mediaInfo: MediaRoute2Info) {
-        val chipState = TransferToThisDeviceTriggered(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString()
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun transferToReceiverSucceeded(
-        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback
-    ) {
-        val chipState = TransferToReceiverSucceeded(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString(),
-            otherDeviceName = otherDeviceInfo.name,
-            undoCallback = undoCallback
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun transferToThisDeviceSucceeded(
-        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback
-    ) {
-        val chipState = TransferToThisDeviceSucceeded(
-            appIconDrawable = fakeAppIconDrawable,
-            appIconContentDescription = mediaInfo.name.toString(),
-            otherDeviceName = otherDeviceInfo.name,
-            undoCallback = undoCallback
-        )
-        controller.displayChip(chipState)
-    }
-
-    private fun noLongerCloseToReceiver() {
-        controller.removeChip()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 7ac9205..4aedbc9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -29,14 +29,16 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.logging.nano.MetricsProto
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED
-import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED
 import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
+import com.android.systemui.qs.dagger.QSScope
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.MultiUserSwitchController
 import com.android.systemui.statusbar.phone.SettingsButton
@@ -54,13 +56,14 @@
  * Main difference between QS and QQS behaviour is condition when buttons should be visible,
  * determined by [buttonsVisibleState]
  */
+@QSScope
 class FooterActionsController @Inject constructor(
     view: FooterActionsView,
+    multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
     private val activityStarter: ActivityStarter,
     private val userManager: UserManager,
     private val userTracker: UserTracker,
     private val userInfoController: UserInfoController,
-    private val multiUserSwitchController: MultiUserSwitchController,
     private val deviceProvisionedController: DeviceProvisionedController,
     private val falsingManager: FalsingManager,
     private val metricsLogger: MetricsLogger,
@@ -68,20 +71,34 @@
     private val globalActionsDialog: GlobalActionsDialogLite,
     private val uiEventLogger: UiEventLogger,
     @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
-    private val buttonsVisibleState: ExpansionState,
     private val globalSetting: GlobalSettings,
-    private val handler: Handler
+    private val handler: Handler,
+    private val featureFlags: FeatureFlags
 ) : ViewController<FooterActionsView>(view) {
 
-    enum class ExpansionState { COLLAPSED, EXPANDED }
-
+    private var lastExpansion = -1f
     private var listening: Boolean = false
 
-    var expanded = false
+    private val alphaAnimator = TouchAnimator.Builder()
+            .addFloat(mView, "alpha", 0f, 1f)
+            .setStartDelay(0.9f)
+            .build()
+
+    var visible = true
+        set(value) {
+            field = value
+            updateVisibility()
+        }
+
+    init {
+        view.elevation = resources.displayMetrics.density * 4f
+        view.setBackgroundColor(Utils.getColorAttrDefaultColor(context, R.attr.underSurfaceColor))
+    }
 
     private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
     private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
     private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
+    private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
 
     private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
         val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -99,9 +116,8 @@
             }
 
     private val onClickListener = View.OnClickListener { v ->
-        // Don't do anything until views are unhidden. Don't do anything if the tap looks
-        // suspicious.
-        if (!buttonsVisible() || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+        // Don't do anything if the tap looks suspicious.
+        if (!visible || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
             return@OnClickListener
         }
         if (v === settingsButton) {
@@ -110,9 +126,7 @@
                 activityStarter.postQSRunnableDismissingKeyguard {}
                 return@OnClickListener
             }
-            metricsLogger.action(
-                    if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
-                    else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH)
+            metricsLogger.action(MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH)
             if (settingsButton.isTunerClick) {
                 activityStarter.postQSRunnableDismissingKeyguard {
                     if (isTunerEnabled()) {
@@ -135,24 +149,14 @@
         }
     }
 
-    private fun buttonsVisible(): Boolean {
-        return when (buttonsVisibleState) {
-            EXPANDED -> expanded
-            COLLAPSED -> !expanded
-        }
-    }
-
     override fun onInit() {
         multiUserSwitchController.init()
     }
 
-    fun hideFooter() {
-        mView.visibility = View.GONE
-    }
-
-    fun showFooter() {
-        mView.visibility = View.VISIBLE
-        updateView()
+    private fun updateVisibility() {
+        val previousVisibility = mView.visibility
+        mView.visibility = if (visible) View.VISIBLE else View.INVISIBLE
+        if (previousVisibility != mView.visibility) updateView()
     }
 
     private fun startSettingsActivity() {
@@ -204,24 +208,23 @@
     }
 
     fun setExpansion(headerExpansionFraction: Float) {
-        mView.setExpansion(headerExpansionFraction)
-    }
-
-    fun updateAnimator(width: Int, numTiles: Int) {
-        mView.updateAnimator(width, numTiles)
-    }
-
-    fun setKeyguardShowing() {
-        mView.setKeyguardShowing()
-    }
-
-    fun refreshVisibility(shouldBeVisible: Boolean) {
-        if (shouldBeVisible) {
-            showFooter()
+        if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+            if (headerExpansionFraction != lastExpansion) {
+                if (headerExpansionFraction >= 1f) {
+                    mView.animate().alpha(1f).setDuration(500L).start()
+                } else if (lastExpansion >= 1f && headerExpansionFraction < 1f) {
+                    mView.animate().alpha(0f).setDuration(250L).start()
+                }
+                lastExpansion = headerExpansionFraction
+            }
         } else {
-            hideFooter()
+            alphaAnimator.setPosition(headerExpansionFraction)
         }
     }
 
+    fun setKeyguardShowing(showing: Boolean) {
+        setExpansion(lastExpansion)
+    }
+
     private fun isTunerEnabled() = tunerService.isTunerEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
deleted file mode 100644
index 7694be5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.systemui.qs
-
-import android.os.Handler
-import android.os.UserManager
-import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.globalactions.GlobalActionsDialogLite
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.FooterActionsController.ExpansionState
-import com.android.systemui.qs.dagger.QSFlagsModule
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.MultiUserSwitchController
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.UserInfoController
-import com.android.systemui.tuner.TunerService
-import com.android.systemui.util.settings.GlobalSettings
-import javax.inject.Inject
-import javax.inject.Named
-
-class FooterActionsControllerBuilder @Inject constructor(
-    private val activityStarter: ActivityStarter,
-    private val userManager: UserManager,
-    private val userTracker: UserTracker,
-    private val userInfoController: UserInfoController,
-    private val multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
-    private val deviceProvisionedController: DeviceProvisionedController,
-    private val falsingManager: FalsingManager,
-    private val metricsLogger: MetricsLogger,
-    private val tunerService: TunerService,
-    private val globalActionsDialog: GlobalActionsDialogLite,
-    private val uiEventLogger: UiEventLogger,
-    @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
-    private val globalSettings: GlobalSettings,
-    @Main private val handler: Handler
-) {
-    private lateinit var view: FooterActionsView
-    private lateinit var buttonsVisibleState: ExpansionState
-
-    fun withView(view: FooterActionsView): FooterActionsControllerBuilder {
-        this.view = view
-        return this
-    }
-
-    fun withButtonsVisibleWhen(state: ExpansionState): FooterActionsControllerBuilder {
-        buttonsVisibleState = state
-        return this
-    }
-
-    fun build(): FooterActionsController {
-        return FooterActionsController(view, activityStarter, userManager,
-                userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
-                deviceProvisionedController, falsingManager, metricsLogger, tunerService,
-                globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
-                globalSettings, handler)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index e6fa2ae..18e0cfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -44,8 +44,6 @@
     private lateinit var multiUserAvatar: ImageView
     private lateinit var tunerIcon: View
 
-    private var settingsCogAnimator: TouchAnimator? = null
-
     private var qsDisabled = false
     private var expansionAmount = 0f
 
@@ -66,19 +64,6 @@
         importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
     }
 
-    fun updateAnimator(width: Int, numTiles: Int) {
-        val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
-                mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
-        val remaining = (width - numTiles * size) / (numTiles - 1)
-        val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
-        val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
-        settingsCogAnimator = TouchAnimator.Builder()
-                .addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
-                .addFloat(settingsButton, "rotation", -120f, 0f)
-                .build()
-        setExpansion(expansionAmount)
-    }
-
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
         updateResources()
@@ -95,15 +80,6 @@
         tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
     }
 
-    fun setKeyguardShowing() {
-        setExpansion(expansionAmount)
-    }
-
-    fun setExpansion(headerExpansionFraction: Float) {
-        expansionAmount = headerExpansionFraction
-        if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
-    }
-
     fun disable(
         state2: Int,
         isTunerEnabled: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index ded6ae0..d1b569f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,9 +14,6 @@
 
 package com.android.systemui.qs;
 
-import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
-
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.util.Log;
@@ -49,7 +46,6 @@
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 /** */
 @QSScope
@@ -88,8 +84,6 @@
     private final QSFgsManagerFooter mFgsManagerFooter;
     private final QSSecurityFooter mSecurityFooter;
     private final QS mQs;
-    private final View mQSFooterActions;
-    private final View mQQSFooterActions;
 
     @Nullable
     private PagedTileLayout mPagedLayout;
@@ -154,16 +148,12 @@
             QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
             QSFgsManagerFooter fgsManagerFooter, QSSecurityFooter securityFooter,
             @Main Executor executor, TunerService tunerService,
-            QSExpansionPathInterpolator qsExpansionPathInterpolator,
-            @Named(QS_FOOTER) FooterActionsView qsFooterActionsView,
-            @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
+            QSExpansionPathInterpolator qsExpansionPathInterpolator) {
         mQs = qs;
         mQuickQsPanel = quickPanel;
         mQsPanelController = qsPanelController;
         mQuickQSPanelController = quickQSPanelController;
         mQuickStatusBarHeader = quickStatusBarHeader;
-        mQQSFooterActions = qqsFooterActionsView;
-        mQSFooterActions = qsFooterActionsView;
         mFgsManagerFooter = fgsManagerFooter;
         mSecurityFooter = securityFooter;
         mHost = qsTileHost;
@@ -476,12 +466,6 @@
                     .setListener(this)
                     .build();
 
-            if (mQQSFooterActions.getVisibility() != View.GONE) {
-                // only when qqs footer is present (which means split shade mode) it needs to
-                // be animated
-                updateQQSFooterAnimation();
-            }
-
             // Fade in the security footer and the divider as we reach the final position
             Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
             builder.addFloat(mFgsManagerFooter.getView(), "alpha", 0, 1);
@@ -627,14 +611,6 @@
         }
     }
 
-    private void updateQQSFooterAnimation() {
-        int translationY = getRelativeTranslationY(mQSFooterActions, mQQSFooterActions);
-        mQQSFooterActionsAnimator = new TouchAnimator.Builder()
-                .addFloat(mQQSFooterActions, "translationY", 0, translationY)
-                .build();
-        mAnimatedQsViews.add(mQSFooterActions);
-    }
-
     private int getRelativeTranslationY(View view1, View view2) {
         int[] qsPosition = new int[2];
         int[] qqsPosition = new int[2];
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e230e1b..7800027 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -19,10 +19,8 @@
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.util.AttributeSet;
 import android.view.View;
@@ -41,7 +39,6 @@
  */
 public class QSContainerImpl extends FrameLayout implements Dumpable {
 
-    private final Point mSizePoint = new Point();
     private int mFancyClippingTop;
     private int mFancyClippingBottom;
     private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
@@ -78,12 +75,6 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mSizePoint.set(0, 0); // Will be retrieved on next measure pass.
-    }
-
-    @Override
     public boolean performClick() {
         // Want to receive clicks so missing QQS tiles doesn't cause collapse, but
         // don't want to do anything with them.
@@ -152,10 +143,10 @@
     void updateResources(QSPanelController qsPanelController,
             QuickStatusBarHeaderController quickStatusBarHeaderController) {
         mQSPanelContainer.setPaddingRelative(
-                getPaddingStart(),
+                mQSPanelContainer.getPaddingStart(),
                 Utils.getQsHeaderSystemIconsAreaHeight(mContext),
-                getPaddingEnd(),
-                getPaddingBottom()
+                mQSPanelContainer.getPaddingEnd(),
+                mQSPanelContainer.getPaddingBottom()
         );
 
         int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
@@ -241,13 +232,6 @@
         }
     }
 
-    private int getDisplayHeight() {
-        if (mSizePoint.y == 0) {
-            getDisplay().getRealSize(mSizePoint);
-        }
-        return mSizePoint.y;
-    }
-
     /**
      * Clip QS bottom using a concave shape.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 0e0681b..aac5672 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -42,11 +42,6 @@
      */
     void setExpansion(float expansion);
 
-    /**
-     * Sets whether or not this footer should set itself to listen for changes in any callbacks
-     * that it has implemented.
-     */
-    void setListening(boolean listening);
 
     /**
      * Sets whether or not the keyguard is currently being shown.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 4622660..6c0ca49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -46,7 +46,6 @@
 public class QSFooterView extends FrameLayout {
     private PageIndicator mPageIndicator;
     private TextView mBuildText;
-    private View mActionsContainer;
     private View mEditButton;
 
     @Nullable
@@ -78,7 +77,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mPageIndicator = findViewById(R.id.footer_page_indicator);
-        mActionsContainer = requireViewById(R.id.qs_footer_actions);
         mBuildText = findViewById(R.id.build);
         mEditButton = findViewById(android.R.id.edit);
 
@@ -105,10 +103,6 @@
         }
     }
 
-    void updateExpansion() {
-        setExpansion(mExpansionAmount);
-    }
-
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -129,7 +123,6 @@
     @Nullable
     private TouchAnimator createFooterAnimator() {
         TouchAnimator.Builder builder = new TouchAnimator.Builder()
-                .addFloat(mActionsContainer, "alpha", 0, 1)
                 .addFloat(mPageIndicator, "alpha", 0, 1)
                 .addFloat(mBuildText, "alpha", 0, 1)
                 .addFloat(mEditButton, "alpha", 0, 1)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 5327b7e..bef4f43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.qs;
 
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
-
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.text.TextUtils;
@@ -33,7 +31,6 @@
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 /**
  * Controller for {@link QSFooterView}.
@@ -43,8 +40,6 @@
 
     private final UserTracker mUserTracker;
     private final QSPanelController mQsPanelController;
-    private final QuickQSPanelController mQuickQSPanelController;
-    private final FooterActionsController mFooterActionsController;
     private final TextView mBuildText;
     private final PageIndicator mPageIndicator;
     private final View mEditButton;
@@ -56,14 +51,10 @@
             UserTracker userTracker,
             FalsingManager falsingManager,
             ActivityStarter activityStarter,
-            QSPanelController qsPanelController,
-            QuickQSPanelController quickQSPanelController,
-            @Named(QS_FOOTER) FooterActionsController footerActionsController) {
+            QSPanelController qsPanelController) {
         super(view);
         mUserTracker = userTracker;
         mQsPanelController = qsPanelController;
-        mQuickQSPanelController = quickQSPanelController;
-        mFooterActionsController = footerActionsController;
         mFalsingManager = falsingManager;
         mActivityStarter = activityStarter;
 
@@ -73,21 +64,7 @@
     }
 
     @Override
-    protected void onInit() {
-        super.onInit();
-        mFooterActionsController.init();
-    }
-
-    @Override
     protected void onViewAttached() {
-        mView.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    mView.updateExpansion();
-                    mFooterActionsController.updateAnimator(right - left,
-                            mQuickQSPanelController.getNumQuickTiles());
-                }
-        );
-
         mBuildText.setOnLongClickListener(view -> {
             CharSequence buildText = mBuildText.getText();
             if (!TextUtils.isEmpty(buildText)) {
@@ -114,9 +91,7 @@
     }
 
     @Override
-    protected void onViewDetached() {
-        setListening(false);
-    }
+    protected void onViewDetached() {}
 
     @Override
     public void setVisibility(int visibility) {
@@ -126,25 +101,17 @@
 
     @Override
     public void setExpanded(boolean expanded) {
-        mFooterActionsController.setExpanded(expanded);
         mView.setExpanded(expanded);
     }
 
     @Override
     public void setExpansion(float expansion) {
         mView.setExpansion(expansion);
-        mFooterActionsController.setExpansion(expansion);
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        mFooterActionsController.setListening(listening);
     }
 
     @Override
     public void setKeyguardShowing(boolean keyguardShowing) {
         mView.setKeyguardShowing();
-        mFooterActionsController.setKeyguardShowing();
     }
 
     /** */
@@ -156,6 +123,5 @@
     @Override
     public void disable(int state1, int state2, boolean animate) {
         mView.disable(state2);
-        mFooterActionsController.disable(state2);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 259b786..50952bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -118,6 +118,7 @@
     private QSPanelController mQSPanelController;
     private QuickQSPanelController mQuickQSPanelController;
     private QSCustomizerController mQSCustomizerController;
+    private FooterActionsController mQSFooterActionController;
     @Nullable
     private ScrollListener mScrollListener;
     /**
@@ -188,9 +189,11 @@
         QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
         mQSPanelController = qsFragmentComponent.getQSPanelController();
         mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
+        mQSFooterActionController = qsFragmentComponent.getQSFooterActionController();
 
         mQSPanelController.init();
         mQuickQSPanelController.init();
+        mQSFooterActionController.init();
 
         mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
         mQSPanelScrollView.addOnLayoutChangeListener(
@@ -380,6 +383,7 @@
         mContainer.disable(state1, state2, animate);
         mHeader.disable(state1, state2, animate);
         mFooter.disable(state1, state2, animate);
+        mQSFooterActionController.disable(state2);
         updateQsState();
     }
 
@@ -396,10 +400,10 @@
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
-        mFooter.setVisibility(!mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
-                || mShowCollapsedOnKeyguard)
-                ? View.VISIBLE
-                : View.INVISIBLE);
+        boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
+                || mShowCollapsedOnKeyguard);
+        mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
+        mQSFooterActionController.setVisible(footerVisible);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling));
         mQSPanelController.setVisibility(
@@ -465,6 +469,7 @@
         }
 
         mFooter.setKeyguardShowing(keyguardShowing);
+        mQSFooterActionController.setKeyguardShowing(keyguardShowing);
         updateQsState();
     }
 
@@ -480,14 +485,13 @@
         if (DEBUG) Log.d(TAG, "setListening " + listening);
         mListening = listening;
         mQSContainerImplController.setListening(listening);
-        mFooter.setListening(listening);
+        mQSFooterActionController.setListening(listening);
         mQSPanelController.setListening(mListening, mQsExpanded);
     }
 
     @Override
     public void setHeaderListening(boolean listening) {
         mQSContainerImplController.setListening(listening);
-        mFooter.setListening(listening);
     }
 
     @Override
@@ -561,6 +565,7 @@
             }
         }
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
+        mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSPanelController.setRevealExpansion(expansion);
         mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
         mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
@@ -711,6 +716,7 @@
         boolean customizing = isCustomizing();
         mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
         mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
+        mQSFooterActionController.setVisible(!customizing);
         mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
         // Let the panel know the position changed and it needs to update where notifications
         // and whatnot are.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 92690c7d..2d2fa1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import com.android.internal.logging.MetricsLogger;
@@ -54,7 +53,6 @@
     // brightness is visible only in split shade
     private final QuickQSBrightnessController mBrightnessController;
     private final BrightnessMirrorHandler mBrightnessMirrorHandler;
-    private final FooterActionsController mFooterActionsController;
 
     @Inject
     QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
@@ -63,14 +61,12 @@
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
             DumpManager dumpManager,
-            QuickQSBrightnessController quickQSBrightnessController,
-            @Named(QQS_FOOTER) FooterActionsController footerActionsController
+            QuickQSBrightnessController quickQSBrightnessController
     ) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager);
         mBrightnessController = quickQSBrightnessController;
         mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
-        mFooterActionsController = footerActionsController;
     }
 
     @Override
@@ -80,8 +76,6 @@
         mMediaHost.setShowsOnlyActiveMedia(true);
         mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
         mBrightnessController.init(mShouldUseSplitNotificationShade);
-        mFooterActionsController.init();
-        mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
     }
 
     @Override
@@ -102,7 +96,6 @@
     void setListening(boolean listening) {
         super.setListening(listening);
         mBrightnessController.setListening(listening);
-        mFooterActionsController.setListening(listening);
     }
 
     public boolean isListening() {
@@ -123,7 +116,6 @@
     @Override
     protected void onConfigurationChanged() {
         mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
-        mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 63cbc21..594f4f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.dagger;
 
+import com.android.systemui.qs.FooterActionsController;
 import com.android.systemui.qs.QSAnimator;
 import com.android.systemui.qs.QSContainerImplController;
 import com.android.systemui.qs.QSFooter;
@@ -61,4 +62,7 @@
 
     /** Construct a {@link QSSquishinessController}. */
     QSSquishinessController getQSSquishinessController();
+
+    /** Construct a {@link FooterActionsController}. */
+    FooterActionsController getQSFooterActionController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 1958caf..776ee10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -21,15 +21,15 @@
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewStub;
 
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.qs.FooterActionsController;
-import com.android.systemui.qs.FooterActionsController.ExpansionState;
-import com.android.systemui.qs.FooterActionsControllerBuilder;
 import com.android.systemui.qs.FooterActionsView;
 import com.android.systemui.qs.QSContainerImpl;
 import com.android.systemui.qs.QSFooter;
@@ -55,8 +55,6 @@
 public interface QSFragmentModule {
     String QS_FGS_MANAGER_FOOTER_VIEW = "qs_fgs_manager_footer";
     String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
-    String QQS_FOOTER = "qqs_footer";
-    String QS_FOOTER = "qs_footer";
     String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
 
     /**
@@ -122,46 +120,26 @@
         return view.findViewById(R.id.qs_footer);
     }
 
-    /** */
+    /**
+     * Provides a {@link FooterActionsView}.
+     *
+     * This will replace a ViewStub either in {@link QSFooterView} or in {@link QSContainerImpl}.
+     */
     @Provides
-    @Named(QS_FOOTER)
-    static FooterActionsView providesQSFooterActionsView(@RootView View view) {
+    static FooterActionsView providesQSFooterActionsView(@RootView View view,
+            FeatureFlags featureFlags) {
+        ViewStub stub;
+        if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+            stub = view.requireViewById(R.id.container_stub);
+        } else {
+            stub = view.requireViewById(R.id.footer_stub);
+        }
+        stub.inflate();
         return view.findViewById(R.id.qs_footer_actions);
     }
 
     /** */
     @Provides
-    @Named(QQS_FOOTER)
-    static FooterActionsView providesQQSFooterActionsView(@RootView View view) {
-        return view.findViewById(R.id.qqs_footer_actions);
-    }
-
-    /** */
-    @Provides
-    @Named(QQS_FOOTER)
-    static FooterActionsController providesQQSFooterActionsController(
-            FooterActionsControllerBuilder footerActionsControllerBuilder,
-            @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
-        return footerActionsControllerBuilder
-                .withView(qqsFooterActionsView)
-                .withButtonsVisibleWhen(ExpansionState.COLLAPSED)
-                .build();
-    }
-
-    /** */
-    @Provides
-    @Named(QS_FOOTER)
-    static FooterActionsController providesQSFooterActionsController(
-            FooterActionsControllerBuilder footerActionsControllerBuilder,
-            @Named(QS_FOOTER) FooterActionsView qsFooterActionsView) {
-        return footerActionsControllerBuilder
-                .withView(qsFooterActionsView)
-                .withButtonsVisibleWhen(ExpansionState.EXPANDED)
-                .build();
-    }
-
-    /** */
-    @Provides
     @QSScope
     static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
         qsFooterViewController.init();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
index dec5afd..c4ea67e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.screenshot;
 
+import static java.util.Objects.requireNonNull;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.drawable.Icon;
@@ -28,10 +30,11 @@
 
 import com.android.systemui.R;
 
+
 /**
  * View for a chip with an icon and text.
  */
-public class ScreenshotActionChip extends FrameLayout {
+public class OverlayActionChip extends FrameLayout {
 
     private static final String TAG = "ScreenshotActionChip";
 
@@ -39,27 +42,27 @@
     private TextView mTextView;
     private boolean mIsPending = false;
 
-    public ScreenshotActionChip(Context context) {
+    public OverlayActionChip(Context context) {
         this(context, null);
     }
 
-    public ScreenshotActionChip(Context context, AttributeSet attrs) {
+    public OverlayActionChip(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public ScreenshotActionChip(Context context, AttributeSet attrs, int defStyleAttr) {
+    public OverlayActionChip(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public ScreenshotActionChip(
+    public OverlayActionChip(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
     @Override
     protected void onFinishInflate() {
-        mIconView = findViewById(R.id.screenshot_action_chip_icon);
-        mTextView = findViewById(R.id.screenshot_action_chip_text);
+        mIconView = requireNonNull(findViewById(R.id.overlay_action_chip_icon));
+        mTextView = requireNonNull(findViewById(R.id.overlay_action_chip_text));
         updatePadding(mTextView.getText().length() > 0);
     }
 
@@ -116,15 +119,15 @@
                 (LinearLayout.LayoutParams) mTextView.getLayoutParams();
         if (hasText) {
             int paddingHorizontal = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.screenshot_action_chip_padding_horizontal);
+                    R.dimen.overlay_action_chip_padding_horizontal);
             int spacing = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.screenshot_action_chip_spacing);
+                    R.dimen.overlay_action_chip_spacing);
             iconParams.setMarginStart(paddingHorizontal);
             iconParams.setMarginEnd(spacing);
             textParams.setMarginEnd(paddingHorizontal);
         } else {
             int paddingHorizontal = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.screenshot_action_chip_icon_only_padding_horizontal);
+                    R.dimen.overlay_action_chip_icon_only_padding_horizontal);
             iconParams.setMarginStart(paddingHorizontal);
             iconParams.setMarginEnd(paddingHorizontal);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 83d8d19..30456a8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -344,7 +344,7 @@
         };
         mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter(
                         ClipboardOverlayController.COPY_OVERLAY_ACTION),
-                ClipboardOverlayController.SELF_PERMISSION, null);
+                ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED);
     }
 
     void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index e5649a1..f982790 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -131,9 +131,9 @@
     private final Resources mResources;
     private final Interpolator mFastOutSlowIn;
     private final DisplayMetrics mDisplayMetrics;
-    private final float mCornerSizeX;
-    private final float mDismissDeltaY;
+    private final float mFixedSize;
     private final AccessibilityManager mAccessibilityManager;
+    private final GestureDetector mSwipeDetector;
 
     private int mNavMode;
     private boolean mOrientationPortrait;
@@ -151,23 +151,21 @@
     private LinearLayout mActionsView;
     private ImageView mBackgroundProtection;
     private FrameLayout mDismissButton;
-    private ScreenshotActionChip mShareChip;
-    private ScreenshotActionChip mEditChip;
-    private ScreenshotActionChip mScrollChip;
-    private ScreenshotActionChip mQuickShareChip;
+    private OverlayActionChip mShareChip;
+    private OverlayActionChip mEditChip;
+    private OverlayActionChip mScrollChip;
+    private OverlayActionChip mQuickShareChip;
 
     private UiEventLogger mUiEventLogger;
     private ScreenshotViewCallback mCallbacks;
-    private Animator mDismissAnimation;
     private boolean mPendingSharedTransition;
-    private GestureDetector mSwipeDetector;
     private SwipeDismissHandler mSwipeDismissHandler;
     private InputMonitorCompat mInputMonitor;
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
     private boolean mShowScrollablePreview;
     private String mPackageName = "";
 
-    private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
+    private final ArrayList<OverlayActionChip> mSmartChips = new ArrayList<>();
     private PendingInteraction mPendingInteraction;
 
     private enum PendingInteraction {
@@ -194,9 +192,7 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mResources = mContext.getResources();
 
-        mCornerSizeX = mResources.getDimensionPixelSize(R.dimen.screenshot_x_scale);
-        mDismissDeltaY = mResources.getDimensionPixelSize(
-                R.dimen.screenshot_dismissal_height_delta);
+        mFixedSize = mResources.getDimensionPixelSize(R.dimen.overlay_x_scale);
 
         // standard material ease
         mFastOutSlowIn =
@@ -474,16 +470,14 @@
         int orientation = mContext.getResources().getConfiguration().orientation;
         mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
         updateInsets(insets);
-        int screenshotFixedSize =
-                mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale);
         ViewGroup.LayoutParams params = mScreenshotPreview.getLayoutParams();
         if (mOrientationPortrait) {
-            params.width = screenshotFixedSize;
+            params.width = (int) mFixedSize;
             params.height = LayoutParams.WRAP_CONTENT;
             mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_START);
         } else {
             params.width = LayoutParams.WRAP_CONTENT;
-            params.height = screenshotFixedSize;
+            params.height = (int) mFixedSize;
             mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_END);
         }
 
@@ -500,7 +494,7 @@
 
         // ratio of preview width, end vs. start size
         float cornerScale =
-                mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
+                mFixedSize / (mOrientationPortrait ? bounds.width() : bounds.height());
         final float currentScale = 1 / cornerScale;
 
         AnimatorSet dropInAnimation = new AnimatorSet();
@@ -651,7 +645,7 @@
         } catch (RemoteException e) {
         }
 
-        ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
+        ArrayList<OverlayActionChip> chips = new ArrayList<>();
 
         mShareChip.setContentDescription(mContext.getString(R.string.screenshot_share_description));
         mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
@@ -716,7 +710,7 @@
                     + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
             mActionsContainer.setScaleX(containerScale);
             mActionsContainerBackground.setScaleX(containerScale);
-            for (ScreenshotActionChip chip : chips) {
+            for (OverlayActionChip chip : chips) {
                 chip.setAlpha(t);
                 chip.setScaleX(1 / containerScale); // invert to keep size of children constant
             }
@@ -772,8 +766,8 @@
             LayoutInflater inflater = LayoutInflater.from(mContext);
 
             for (Notification.Action smartAction : imageData.smartActions) {
-                ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
-                        R.layout.screenshot_action_chip, mActionsView, false);
+                OverlayActionChip actionChip = (OverlayActionChip) inflater.inflate(
+                        R.layout.overlay_action_chip, mActionsView, false);
                 actionChip.setText(smartAction.title);
                 actionChip.setIcon(smartAction.getIcon(), false);
                 actionChip.setPendingIntent(smartAction.actionIntent,
@@ -792,8 +786,8 @@
     void addQuickShareChip(Notification.Action quickShareAction) {
         if (mPendingInteraction == null) {
             LayoutInflater inflater = LayoutInflater.from(mContext);
-            mQuickShareChip = (ScreenshotActionChip) inflater.inflate(
-                    R.layout.screenshot_action_chip, mActionsView, false);
+            mQuickShareChip = (OverlayActionChip) inflater.inflate(
+                    R.layout.overlay_action_chip, mActionsView, false);
             mQuickShareChip.setText(quickShareAction.title);
             mQuickShareChip.setIcon(quickShareAction.getIcon(), false);
             mQuickShareChip.setOnClickListener(v -> {
@@ -894,7 +888,7 @@
         if (mShowScrollablePreview) {
             Rect scrollableArea = scrollableAreaOnScreen(response);
 
-            float scale = mCornerSizeX
+            float scale = mFixedSize
                     / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight());
             ConstraintLayout.LayoutParams params =
                     (ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams();
@@ -945,7 +939,7 @@
     }
 
     boolean isDismissing() {
-        return (mDismissAnimation != null && mDismissAnimation.isRunning());
+        return mSwipeDismissHandler.isDismissing();
     }
 
     boolean isPendingSharedTransition() {
@@ -961,12 +955,6 @@
             Log.d(TAG, "reset screenshot view");
         }
 
-        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
-            if (DEBUG_ANIM) {
-                Log.d(TAG, "cancelling dismiss animation");
-            }
-            mDismissAnimation.cancel();
-        }
         mSwipeDismissHandler.cancel();
         if (DEBUG_WINDOW) {
             Log.d(TAG, "removing OnComputeInternalInsetsListener");
@@ -994,7 +982,7 @@
         mShareChip.setIsPending(false);
         mEditChip.setIsPending(false);
         mPendingInteraction = null;
-        for (ScreenshotActionChip chip : mSmartChips) {
+        for (OverlayActionChip chip : mSmartChips) {
             mActionsView.removeView(chip);
         }
         mSmartChips.clear();
@@ -1019,31 +1007,6 @@
         }
     }
 
-    private AnimatorSet createScreenshotTranslateDismissAnimation() {
-        ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
-        alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
-        alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
-        alphaAnim.addUpdateListener(animation -> {
-            setAlpha(1 - animation.getAnimatedFraction());
-        });
-
-        ValueAnimator xAnim = ValueAnimator.ofFloat(0, 1);
-        xAnim.setInterpolator(mAccelerateInterpolator);
-        xAnim.setDuration(SCREENSHOT_DISMISS_X_DURATION_MS);
-        float deltaX = mDirectionLTR
-                ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth())
-                : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX());
-        xAnim.addUpdateListener(animation -> {
-            float currXDelta = MathUtils.lerp(0, deltaX, animation.getAnimatedFraction());
-            mScreenshotStatic.setTranslationX(currXDelta);
-        });
-
-        AnimatorSet animSet = new AnimatorSet();
-        animSet.play(xAnim).with(alphaAnim);
-
-        return animSet;
-    }
-
     ValueAnimator createScreenshotFadeDismissAnimation() {
         ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
         alphaAnim.addUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
index 4e96003..451fb13 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.screenshot;
 
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
 
 import android.animation.Animator;
@@ -137,10 +138,20 @@
     }
 
     /**
+     * Return whether the view is currently being dismissed
+     */
+    public boolean isDismissing() {
+        return (mDismissAnimation != null && mDismissAnimation.isRunning());
+    }
+
+    /**
      * Cancel the currently-running dismissal animation, if any.
      */
     public void cancel() {
-        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+        if (isDismissing()) {
+            if (DEBUG_ANIM) {
+                Log.d(TAG, "cancelling dismiss animation");
+            }
             mDismissAnimation.cancel();
         }
     }
@@ -182,7 +193,13 @@
         // make sure the UI gets all the way off the screen in the direction of movement
         // (the actions container background is guaranteed to be both the leftmost and
         // rightmost UI element in LTR and RTL)
-        float finalX = startX <= 0 ? -1 * mView.getRight() : mDisplayMetrics.widthPixels;
+        float finalX;
+        int layoutDir = mView.getContext().getResources().getConfiguration().getLayoutDirection();
+        if (startX > 0 || (startX == 0 && layoutDir == View.LAYOUT_DIRECTION_RTL)) {
+            finalX = mDisplayMetrics.widthPixels;
+        } else {
+            finalX = -1 * mView.getRight();
+        }
         float distance = Math.abs(finalX - startX);
 
         anim.addUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 9d43d30..2f5eaa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -43,6 +43,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.media.MediaRoute2Info;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -63,6 +64,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IAddTileResultCallback;
 import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.GcUtils;
 import com.android.internal.view.AppearanceRegion;
@@ -156,6 +158,8 @@
     private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT;
     private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT;
     private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT;
+    private static final int MSG_MEDIA_TRANSFER_SENDER_STATE = 64 << MSG_SHIFT;
+    private static final int MSG_MEDIA_TRANSFER_RECEIVER_STATE = 65 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -439,6 +443,17 @@
          * @see IStatusBar#cancelRequestAddTile
          */
         default void cancelRequestAddTile(@NonNull String packageName) {}
+
+        /** @see IStatusBar#updateMediaTapToTransferSenderDisplay */
+        default void updateMediaTapToTransferSenderDisplay(
+                @StatusBarManager.MediaTransferSenderState int displayState,
+                @NonNull MediaRoute2Info routeInfo,
+                @Nullable IUndoMediaTransferCallback undoCallback) {}
+
+        /** @see IStatusBar#updateMediaTapToTransferReceiverDisplay */
+        default void updateMediaTapToTransferReceiverDisplay(
+                @StatusBarManager.MediaTransferReceiverState int displayState,
+                @NonNull MediaRoute2Info routeInfo) {}
     }
 
     public CommandQueue(Context context) {
@@ -1177,6 +1192,29 @@
         mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_CANCEL, s).sendToTarget();
     }
 
+    @Override
+    public void updateMediaTapToTransferSenderDisplay(
+            @StatusBarManager.MediaTransferSenderState int displayState,
+            MediaRoute2Info routeInfo,
+            IUndoMediaTransferCallback undoCallback
+    ) throws RemoteException {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = displayState;
+        args.arg2 = routeInfo;
+        args.arg3 = undoCallback;
+        mHandler.obtainMessage(MSG_MEDIA_TRANSFER_SENDER_STATE, args).sendToTarget();
+    }
+
+    @Override
+    public void updateMediaTapToTransferReceiverDisplay(
+            int displayState,
+            MediaRoute2Info routeInfo) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = displayState;
+        args.arg2 = routeInfo;
+        mHandler.obtainMessage(MSG_MEDIA_TRANSFER_RECEIVER_STATE, args).sendToTarget();
+    }
+
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -1574,6 +1612,29 @@
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).cancelRequestAddTile(packageName);
                     }
+                    break;
+                case MSG_MEDIA_TRANSFER_SENDER_STATE:
+                    args = (SomeArgs) msg.obj;
+                    int displayState = (int) args.arg1;
+                    MediaRoute2Info routeInfo = (MediaRoute2Info) args.arg2;
+                    IUndoMediaTransferCallback undoCallback =
+                            (IUndoMediaTransferCallback) args.arg3;
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).updateMediaTapToTransferSenderDisplay(
+                                displayState, routeInfo, undoCallback);
+                    }
+                    args.recycle();
+                    break;
+                case MSG_MEDIA_TRANSFER_RECEIVER_STATE:
+                    args = (SomeArgs) msg.obj;
+                    int receiverDisplayState = (int) args.arg1;
+                    MediaRoute2Info receiverRouteInfo = (MediaRoute2Info) args.arg2;
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).updateMediaTapToTransferReceiverDisplay(
+                                receiverDisplayState, receiverRouteInfo);
+                    }
+                    args.recycle();
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 391525e..092e86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -179,6 +179,7 @@
         if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) {
             return;
         }
+        Trace.beginSection("NotificationViewHierarchyManager.updateNotificationViews");
 
         beginUpdate();
 
@@ -353,6 +354,7 @@
         mListContainer.onNotificationViewUpdateFinished();
 
         endUpdate();
+        Trace.endSection();
     }
 
     /**
@@ -362,6 +364,7 @@
      * {@link com.android.systemui.statusbar.notification.collection.coordinator.StackCoordinator}
      */
     private void updateNotifStats() {
+        Trace.beginSection("NotificationViewHierarchyManager.updateNotifStats");
         boolean hasNonClearableAlertingNotifs = false;
         boolean hasClearableAlertingNotifs = false;
         boolean hasNonClearableSilentNotifs = false;
@@ -403,6 +406,7 @@
                 hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */,
                 hasClearableSilentNotifs /* hasClearableSilentNotifs */
         ));
+        Trace.endSection();
     }
 
     /**
@@ -520,7 +524,7 @@
     }
 
     private void updateRowStatesInternal() {
-        Trace.beginSection("NotificationViewHierarchyManager#updateRowStates");
+        Trace.beginSection("NotificationViewHierarchyManager.updateRowStates");
         final int N = mListContainer.getContainerChildCount();
 
         int visibleNotifications = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
index c0148c0..16bc951e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
@@ -37,12 +37,6 @@
      */
     public static final int SHADE_LOCKED = 2;
 
-    /**
-     * Status bar is locked and shows the full screen user switcher.
-     */
-    public static final int FULLSCREEN_USER_SWITCHER = 3;
-
-
     public static String toShortString(int x) {
         switch (x) {
             case SHADE:
@@ -51,8 +45,6 @@
                 return "SHD_LCK";
             case KEYGUARD:
                 return "KGRD";
-            case FULLSCREEN_USER_SWITCHER:
-                return "FS_USRSW";
             default:
                 return "bad_value_" + x;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index f56602e..ee12cc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -76,7 +76,7 @@
     // Must be a power of 2
     private static final int HISTORY_SIZE = 32;
 
-    private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
+    private static final int MAX_STATE = StatusBarState.SHADE_LOCKED;
     private static final int MIN_STATE = StatusBarState.SHADE;
 
     private static final Comparator<RankedListener> sComparator =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java
index 8330169..b66a48e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java
@@ -34,10 +34,7 @@
     STATUS_BAR_STATE_KEYGUARD(430),
 
     @UiEvent(doc = "StatusBarState changed to SHADE_LOCKED state")
-    STATUS_BAR_STATE_SHADE_LOCKED(431),
-
-    @UiEvent(doc = "StatusBarState changed to FULLSCREEN_USER_SWITCHER state")
-    STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER(432);
+    STATUS_BAR_STATE_SHADE_LOCKED(431);
 
     private int mId;
     StatusBarStateEvent(int id) {
@@ -60,8 +57,6 @@
                 return STATUS_BAR_STATE_KEYGUARD;
             case StatusBarState.SHADE_LOCKED:
                 return STATUS_BAR_STATE_SHADE_LOCKED;
-            case StatusBarState.FULLSCREEN_USER_SWITCHER:
-                return STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER;
             default:
                 return STATUS_BAR_STATE_UNKNOWN;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c331608..ad9f12e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,6 +24,7 @@
 import android.app.NotificationChannel;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -343,11 +344,14 @@
     private final InflationCallback mInflationCallback = new InflationCallback() {
         @Override
         public void handleInflationException(NotificationEntry entry, Exception e) {
+            Trace.beginSection("NotificationEntryManager.handleInflationException");
             NotificationEntryManager.this.handleInflationException(entry.getSbn(), e);
+            Trace.endSection();
         }
 
         @Override
         public void onAsyncInflationFinished(NotificationEntry entry) {
+            Trace.beginSection("NotificationEntryManager.onAsyncInflationFinished");
             mPendingNotifications.remove(entry.getKey());
             // If there was an async task started after the removal, we don't want to add it back to
             // the list, otherwise we might get leaks.
@@ -369,6 +373,7 @@
                     }
                 }
             }
+            Trace.endSection();
         }
     };
 
@@ -463,6 +468,7 @@
             boolean forceRemove,
             DismissedByUserStats dismissedByUserStats,
             int reason) {
+        Trace.beginSection("NotificationEntryManager.removeNotificationInternal");
 
         final NotificationEntry entry = getActiveNotificationUnfiltered(key);
 
@@ -470,6 +476,7 @@
             if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
                 // Remove intercepted; log and skip
                 mLogger.logRemovalIntercepted(key);
+                Trace.endSection();
                 return;
             }
         }
@@ -557,6 +564,7 @@
                 mLeakDetector.trackGarbage(entry);
             }
         }
+        Trace.endSection();
     }
 
     private void sendNotificationRemovalToServer(
@@ -620,6 +628,7 @@
     private void addNotificationInternal(
             StatusBarNotification notification,
             RankingMap rankingMap) throws InflationException {
+        Trace.beginSection("NotificationEntryManager.addNotificationInternal");
         String key = notification.getKey();
         if (DEBUG) {
             Log.d(TAG, "addNotification key=" + key);
@@ -667,6 +676,7 @@
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onRankingApplied();
         }
+        Trace.endSection();
     }
 
     public void addNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -679,12 +689,14 @@
 
     private void updateNotificationInternal(StatusBarNotification notification,
             RankingMap ranking) throws InflationException {
+        Trace.beginSection("NotificationEntryManager.updateNotificationInternal");
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
         abortExistingInflation(key, "updateNotification");
         final NotificationEntry entry = getActiveNotificationUnfiltered(key);
         if (entry == null) {
+            Trace.endSection();
             return;
         }
 
@@ -721,6 +733,7 @@
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onRankingApplied();
         }
+        Trace.endSection();
     }
 
     public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -740,14 +753,17 @@
             mLogger.logUseWhileNewPipelineActive("updateNotifications", reason);
             return;
         }
+        Trace.beginSection("NotificationEntryManager.updateNotifications");
         reapplyFilterAndSort(reason);
         if (mPresenter != null) {
             mPresenter.updateNotificationViews(reason);
         }
         mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications());
+        Trace.endSection();
     }
 
     public void updateNotificationRanking(RankingMap rankingMap) {
+        Trace.beginSection("NotificationEntryManager.updateNotificationRanking");
         List<NotificationEntry> entries = new ArrayList<>();
         entries.addAll(getVisibleNotifications());
         entries.addAll(mPendingNotifications.values());
@@ -788,6 +804,7 @@
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onRankingApplied();
         }
+        Trace.endSection();
     }
 
     void notifyChannelModified(
@@ -887,6 +904,7 @@
 
     /** @return list of active notifications filtered for the current user */
     public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
+        Trace.beginSection("NotificationEntryManager.getActiveNotificationsForCurrentUser");
         Assert.isMainThread();
         ArrayList<NotificationEntry> filtered = new ArrayList<>();
 
@@ -898,7 +916,7 @@
             }
             filtered.add(entry);
         }
-
+        Trace.endSection();
         return filtered;
     }
 
@@ -908,10 +926,12 @@
      * @param reason the reason for calling this method, which will be logged
      */
     public void updateRanking(RankingMap rankingMap, String reason) {
+        Trace.beginSection("NotificationEntryManager.updateRanking");
         updateRankingAndSort(rankingMap, reason);
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onRankingApplied();
         }
+        Trace.endSection();
     }
 
     /** Resorts / filters the current notification set with the current RankingMap */
@@ -920,7 +940,9 @@
             mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason);
             return;
         }
+        Trace.beginSection("NotificationEntryManager.reapplyFilterAndSort");
         updateRankingAndSort(mRanker.getRankingMap(), reason);
+        Trace.endSection();
     }
 
     /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
@@ -929,9 +951,11 @@
             mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason);
             return;
         }
+        Trace.beginSection("NotificationEntryManager.updateRankingAndSort");
         mSortedAndFiltered.clear();
         mSortedAndFiltered.addAll(mRanker.updateRanking(
                 rankingMap, mActiveNotifications.values(), reason));
+        Trace.endSection();
     }
 
     /** dump the current active notification list. Called from StatusBar */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 74c97fd..31d9f09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -426,14 +426,18 @@
         }
         Trace.endSection();
 
+        Trace.beginSection("ShadeListBuilder.logEndBuildList");
         // Step 9: We're done!
         mLogger.logEndBuildList(
                 mIterationCount,
                 mReadOnlyNotifList.size(),
                 countChildren(mReadOnlyNotifList));
         if (mAlwaysLogList || mIterationCount % 10 == 0) {
+            Trace.beginSection("ShadeListBuilder.logFinalList");
             mLogger.logFinalList(mNotifList);
+            Trace.endSection();
         }
+        Trace.endSection();
         mPipelineState.setState(STATE_IDLE);
         mIterationCount++;
         Trace.endSection();
@@ -996,16 +1000,20 @@
     }
 
     private void freeEmptyGroups() {
+        Trace.beginSection("ShadeListBuilder.freeEmptyGroups");
         mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
+        Trace.endSection();
     }
 
     private void logChanges() {
+        Trace.beginSection("ShadeListBuilder.logChanges");
         for (NotificationEntry entry : mAllEntries) {
             logAttachStateChanges(entry);
         }
         for (GroupEntry group : mGroups.values()) {
             logAttachStateChanges(group);
         }
+        Trace.endSection();
     }
 
     private void logAttachStateChanges(ListEntry entry) {
@@ -1083,6 +1091,7 @@
     }
 
     private void cleanupPluggables() {
+        Trace.beginSection("ShadeListBuilder.cleanupPluggables");
         callOnCleanup(mNotifPreGroupFilters);
         callOnCleanup(mNotifPromoters);
         callOnCleanup(mNotifFinalizeFilters);
@@ -1093,6 +1102,7 @@
         }
 
         callOnCleanup(List.of(getStabilityManager()));
+        Trace.endSection();
     }
 
     private void callOnCleanup(List<? extends Pluggable<?>> pluggables) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index c6a8a69..1c96e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
 /**
@@ -38,10 +39,11 @@
         pipeline.addOnAfterRenderListListener(::onAfterRenderList)
     }
 
-    fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) {
-        controller.setNotifStats(calculateNotifStats(entries))
-        notificationIconAreaController.updateNotificationIcons(entries)
-    }
+    fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
+        traceSection("StackCoordinator.onAfterRenderList") {
+            controller.setNotifStats(calculateNotifStats(entries))
+            notificationIconAreaController.updateNotificationIcons(entries)
+        }
 
     private fun calculateNotifStats(entries: List<ListEntry>): NotifStats {
         var hasNonClearableAlertingNotifs = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index b02dc0c..54e26c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.util.children
 import com.android.systemui.util.foldToSparseArray
 import com.android.systemui.util.takeUntil
+import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
 /**
@@ -157,7 +158,9 @@
             }
         }
     }
-    private fun logShadeContents() = parent.children.forEachIndexed(::logShadeChild)
+    private fun logShadeContents() = traceSection("NotifSectionsManager.logShadeContents") {
+        parent.children.forEachIndexed(::logShadeChild)
+    }
 
     private val isUsingMultipleSections: Boolean
         get() = sectionsFeatureManager.getNumberOfBuckets() > 1
@@ -221,10 +224,10 @@
      * Should be called whenever notifs are added, removed, or updated. Updates section boundary
      * bookkeeping and adds/moves/removes section headers if appropriate.
      */
-    fun updateSectionBoundaries(reason: String) {
+    fun updateSectionBoundaries(reason: String) = traceSection("NotifSectionsManager.update") {
         notifPipelineFlags.checkLegacyPipelineEnabled()
         if (!isUsingMultipleSections) {
-            return
+            return@traceSection
         }
         logger.logStartSectionUpdate(reason)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dd72615..25b8a65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -43,7 +43,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.IndentingPrintWriter;
@@ -696,8 +695,7 @@
                 && mQsExpansionFraction != 1
                 && !mScreenOffAnimationController.shouldHideNotificationsFooter()
                 && !mIsRemoteInputActive;
-        boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
+        boolean showHistory = mController.isHistoryEnabled();
 
         updateFooterView(showFooterView, showDismissView, showHistory);
     }
@@ -5243,11 +5241,10 @@
                 R.layout.status_bar_no_notifications, this, false);
         view.setText(R.string.empty_shade_text);
         view.setOnClickListener(v -> {
-            final boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
-            Intent intent = showHistory ? new Intent(
-                    Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
-                    Settings.ACTION_NOTIFICATION_SETTINGS);
+            final boolean showHistory = mController.isHistoryEnabled();
+            Intent intent = showHistory
+                    ? new Intent(Settings.ACTION_NOTIFICATION_HISTORY)
+                    : new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
             mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
         });
         setEmptyShadeView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 334128a..a2929f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -35,6 +35,8 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.os.Trace;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -180,7 +182,6 @@
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     // TODO: StatusBar should be encapsulated behind a Controller
     private final StatusBar mStatusBar;
-    private final NotificationGroupManagerLegacy mLegacyGroupManager;
     private final SectionHeaderController mSilentHeaderController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final InteractionJankMonitor mJankMonitor;
@@ -191,6 +192,7 @@
     private boolean mFadeNotificationsOnDismiss;
     private NotificationSwipeHelper mSwipeHelper;
     private boolean mShowEmptyShadeView;
+    @Nullable private Boolean mHistoryEnabled;
     private int mBarState;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
@@ -340,6 +342,8 @@
         @Override
         public void onUserChanged(int userId) {
             mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+            mHistoryEnabled = null;
+            updateFooter();
         }
     };
 
@@ -699,8 +703,6 @@
             }
         });
         mNotifPipelineFlags = notifPipelineFlags;
-        mLegacyGroupManager = mNotifPipelineFlags.isNewPipelineEnabled()
-                ? null : legacyGroupManager;
         mSilentHeaderController = silentHeaderController;
         mNotifPipeline = notifPipeline;
         mNotifCollection = notifCollection;
@@ -802,6 +804,7 @@
                 (key, newValue) -> {
                     switch (key) {
                         case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
+                            mHistoryEnabled = null;  // invalidate
                             updateFooter();
                             break;
                         case HIGH_PRIORITY:
@@ -1009,6 +1012,20 @@
         return mNotifStats.getNumActiveNotifs();
     }
 
+    public boolean isHistoryEnabled() {
+        Boolean historyEnabled = mHistoryEnabled;
+        if (historyEnabled == null) {
+            if (mView == null || mView.getContext() == null) {
+                Log.wtf(TAG, "isHistoryEnabled failed to initialize its value");
+                return false;
+            }
+            mHistoryEnabled = historyEnabled =
+                    Settings.Secure.getIntForUser(mView.getContext().getContentResolver(),
+                    Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
+        }
+        return historyEnabled;
+    }
+
     public int getIntrinsicContentHeight() {
         return mView.getIntrinsicContentHeight();
     }
@@ -1199,6 +1216,7 @@
      * are true.
      */
     public void updateShowEmptyShadeView() {
+        Trace.beginSection("NSSLC.updateShowEmptyShadeView");
         mShowEmptyShadeView = mBarState != KEYGUARD
                 && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
                 && getVisibleNotificationCount() == 0;
@@ -1206,6 +1224,7 @@
         mView.updateEmptyShadeView(
                 mShowEmptyShadeView,
                 mZenModeController.areNotificationsHiddenInShade());
+        Trace.endSection();
     }
 
     public boolean areNotificationsHiddenInShade() {
@@ -1323,11 +1342,15 @@
         if (mNotifPipelineFlags.isNewPipelineEnabled()) {
             return;
         }
+        Trace.beginSection("NSSLC.updateSectionBoundaries");
         mView.updateSectionBoundaries(reason);
+        Trace.endSection();
     }
 
     public void updateFooter() {
+        Trace.beginSection("NSSLC.updateFooter");
         mView.updateFooter();
+        Trace.endSection();
     }
 
     public void onUpdateRowStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 04d3e9a..6a78370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -180,6 +180,7 @@
     private final MetricsLogger mMetricsLogger;
     private final AuthController mAuthController;
     private final StatusBarStateController mStatusBarStateController;
+    private final LatencyTracker mLatencyTracker;
 
     private long mLastFpFailureUptimeMillis;
     private int mNumConsecutiveFpFailures;
@@ -281,7 +282,8 @@
             AuthController authController,
             StatusBarStateController statusBarStateController,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
-            SessionTracker sessionTracker) {
+            SessionTracker sessionTracker,
+            LatencyTracker latencyTracker) {
         mContext = context;
         mPowerManager = powerManager;
         mShadeController = shadeController;
@@ -289,6 +291,7 @@
         mDozeParameters = dozeParameters;
         mUpdateMonitor.registerCallback(this);
         mMediaManager = notificationMediaManager;
+        mLatencyTracker = latencyTracker;
         wakefulnessLifecycle.addObserver(mWakefulnessObserver);
         screenLifecycle.addObserver(mScreenObserver);
 
@@ -343,13 +346,13 @@
     public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
         Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
         releaseBiometricWakeLock();
-        if (!mUpdateMonitor.isDeviceInteractive()) {
-            if (LatencyTracker.isEnabled(mContext)) {
+        if (isWakeAndUnlock()) {
+            if (mLatencyTracker.isEnabled()) {
                 int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
                 if (biometricSourceType == BiometricSourceType.FACE) {
                     action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
                 }
-                LatencyTracker.getInstance(mContext).onActionStart(action);
+                mLatencyTracker.onActionStart(action);
             }
             mWakeLock = mPowerManager.newWakeLock(
                     PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME);
@@ -652,6 +655,14 @@
         Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
                 .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
 
+        if (mLatencyTracker.isEnabled()) {
+            int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+            if (biometricSourceType == BiometricSourceType.FACE) {
+                action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
+            }
+            mLatencyTracker.onActionCancel(action);
+        }
+
         if (biometricSourceType == BiometricSourceType.FINGERPRINT
                 && mUpdateMonitor.isUdfpsSupported()) {
             long currUptimeMillis = SystemClock.uptimeMillis();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
index 1a88533..b457ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -1,6 +1,8 @@
 package com.android.systemui.statusbar.phone
 
 import android.view.WindowInsets
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
@@ -14,7 +16,8 @@
 class NotificationsQSContainerController @Inject constructor(
     view: NotificationsQuickSettingsContainer,
     private val navigationModeController: NavigationModeController,
-    private val overviewProxyService: OverviewProxyService
+    private val overviewProxyService: OverviewProxyService,
+    private val featureFlags: FeatureFlags
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
     var qsExpanded = false
@@ -108,7 +111,11 @@
         }
         mView.setPadding(0, 0, 0, containerPadding)
         mView.setNotificationsMarginBottom(notificationsMargin)
-        mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
+        if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+            mView.setQSContainerPaddingBottom(notificationsMargin)
+        } else {
+            mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
+        }
     }
 
     private fun calculateBottomSpacing(): Pair<Int, Int> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index cecbcdb..95e3b70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -52,6 +52,7 @@
     private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
     private QS mQs;
     private View mQSScrollView;
+    private View mQSContainer;
 
     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -70,6 +71,7 @@
         mQs = (QS) fragment;
         mQSFragmentAttachedListener.accept(mQs);
         mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
+        mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
     }
 
     @Override
@@ -89,6 +91,16 @@
                     mQSScrollView.getPaddingLeft(),
                     mQSScrollView.getPaddingTop(),
                     mQSScrollView.getPaddingRight(),
+                    paddingBottom);
+        }
+    }
+
+    public void setQSContainerPaddingBottom(int paddingBottom) {
+        if (mQSContainer != null) {
+            mQSContainer.setPadding(
+                    mQSContainer.getPaddingLeft(),
+                    mQSContainer.getPaddingTop(),
+                    mQSContainer.getPaddingRight(),
                     paddingBottom
             );
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 1cb19ab..d6bf5f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -49,29 +49,29 @@
     }
 
     override fun onViewAttached() {
-        moveFromCenterAnimationController?.let { animationController ->
-            val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
-            val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
+        if (moveFromCenterAnimationController == null) return
 
-            val viewsToAnimate = arrayOf(
-                statusBarLeftSide,
-                systemIconArea
-            )
+        val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
+        val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
 
-            mView.viewTreeObserver.addOnPreDrawListener(object :
-                ViewTreeObserver.OnPreDrawListener {
-                override fun onPreDraw(): Boolean {
-                    animationController.onViewsReady(viewsToAnimate)
-                    mView.viewTreeObserver.removeOnPreDrawListener(this)
-                    return true
-                }
-            })
+        val viewsToAnimate = arrayOf(
+            statusBarLeftSide,
+            systemIconArea
+        )
 
-            mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
-                val widthChanged = right - left != oldRight - oldLeft
-                if (widthChanged) {
-                    moveFromCenterAnimationController.onStatusBarWidthChanged()
-                }
+        mView.viewTreeObserver.addOnPreDrawListener(object :
+            ViewTreeObserver.OnPreDrawListener {
+            override fun onPreDraw(): Boolean {
+                moveFromCenterAnimationController.onViewsReady(viewsToAnimate)
+                mView.viewTreeObserver.removeOnPreDrawListener(this)
+                return true
+            }
+        })
+
+        mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
+            val widthChanged = right - left != oldRight - oldLeft
+            if (widthChanged) {
+                moveFromCenterAnimationController.onStatusBarWidthChanged()
             }
         }
 
@@ -162,9 +162,7 @@
             PhoneStatusBarViewController(
                 view,
                 progressProvider.getOrNull(),
-                unfoldComponent.map {
-                    it.getStatusBarMoveFromCenterAnimationController()
-                }.getOrNull(),
+                unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
                 touchEventHandler,
                 configurationController
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2f3300a..c8cc807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2935,14 +2935,6 @@
         return updateIsKeyguard();
     }
 
-    /**
-     * stop(tag)
-     * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER.
-     */
-    public boolean isFullScreenUserSwitcherState() {
-        return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
-    }
-
     boolean updateIsKeyguard() {
         return updateIsKeyguard(false /* forceStateChange */);
     }
@@ -2996,9 +2988,7 @@
             onLaunchTransitionFadingEnded();
         }
         mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
-        if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
-            mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
-        } else if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
+        if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
         updatePanelExpansionForKeyguard();
@@ -3009,8 +2999,6 @@
         if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
                 != BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) {
             mShadeController.instantExpandNotificationsPanel();
-        } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
-            instantCollapseNotificationPanel();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b833c89..d42a423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -49,7 +49,6 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.DejankUtils;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dreams.DreamOverlayStateController;
@@ -214,6 +213,7 @@
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final DockManager mDockManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateManager;
+    private final LatencyTracker mLatencyTracker;
     private KeyguardBypassController mBypassController;
     @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
 
@@ -246,7 +246,8 @@
             NotificationMediaManager notificationMediaManager,
             KeyguardBouncer.Factory keyguardBouncerFactory,
             KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
-            Lazy<ShadeController> shadeController) {
+            Lazy<ShadeController> shadeController,
+            LatencyTracker latencyTracker) {
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -262,6 +263,7 @@
         mKeyguardBouncerFactory = keyguardBouncerFactory;
         mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
         mShadeController = shadeController;
+        mLatencyTracker = latencyTracker;
     }
 
     @Override
@@ -859,15 +861,11 @@
     }
 
     private void wakeAndUnlockDejank() {
-        if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
-                && LatencyTracker.isEnabled(mContext)) {
+        if (mBiometricUnlockController.isWakeAndUnlock() && mLatencyTracker.isEnabled()) {
             BiometricSourceType type = mBiometricUnlockController.getBiometricType();
-            DejankUtils.postAfterTraversal(() -> {
-                    LatencyTracker.getInstance(mContext).onActionEnd(
-                            type == BiometricSourceType.FACE
-                                    ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK
-                                    : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
-            });
+            mLatencyTracker.onActionEnd(type == BiometricSourceType.FACE
+                            ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK
+                            : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
         }
     }
 
@@ -1186,7 +1184,6 @@
         // When a dream overlay is active, scrimming will cause any expansion to immediately expand.
         return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
                 || mBouncer.willDismissWithAction()
-                || mStatusBar.isFullScreenUserSwitcherState()
                 || (mBouncer.isShowing() && mBouncer.isScrimmed())
                 || mBouncer.isFullscreenBouncer();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 6d033477..79c0984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -18,11 +18,13 @@
 import android.view.View
 import android.view.WindowManager
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.AlphaProvider
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.StatusBarViewsCenterProvider
 import com.android.systemui.unfold.SysUIUnfoldScope
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import javax.inject.Inject
+import kotlin.math.max
 
 @SysUIUnfoldScope
 class StatusBarMoveFromCenterAnimationController @Inject constructor(
@@ -31,8 +33,11 @@
 ) {
 
     private val transitionListener = TransitionListener()
-    private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
-        viewCenterProvider = StatusBarViewsCenterProvider())
+    private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(
+        windowManager,
+        viewCenterProvider = StatusBarViewsCenterProvider(),
+        alphaProvider = StatusBarIconsAlphaProvider()
+    )
 
     fun onViewsReady(viewsToAnimate: Array<View>) {
         moveFromCenterAnimator.updateDisplayProperties()
@@ -65,4 +70,15 @@
             moveFromCenterAnimator.onTransitionProgress(1f)
         }
     }
+
+    private class StatusBarIconsAlphaProvider : AlphaProvider {
+        override fun getAlpha(progress: Float): Float {
+            return max(
+                0f,
+                (progress - ICONS_START_APPEARING_PROGRESS) / (1 - ICONS_START_APPEARING_PROGRESS)
+            )
+        }
+    }
 }
+
+private const val ICONS_START_APPEARING_PROGRESS = 0.75F
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 49e712d..1b73595 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -19,7 +19,6 @@
 import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -427,22 +426,6 @@
         return mSimpleUserSwitcher;
     }
 
-    public boolean useFullscreenUserSwitcher() {
-        // Use adb to override:
-        // adb shell settings put system enable_fullscreen_user_switcher 0  # Turn it off.
-        // adb shell settings put system enable_fullscreen_user_switcher 1  # Turn it on.
-        // Restart SystemUI or adb reboot.
-        final int DEFAULT = -1;
-        final int overrideUseFullscreenUserSwitcher =
-                whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(),
-                        "enable_fullscreen_user_switcher", DEFAULT));
-        if (overrideUseFullscreenUserSwitcher != DEFAULT) {
-            return overrideUseFullscreenUserSwitcher != 0;
-        }
-        // Otherwise default to the build setting.
-        return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
-    }
-
     public void setResumeUserOnGuestLogout(boolean resume) {
         mResumeUserOnGuestLogout = resume;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 43d9a75..dc7026d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -95,7 +95,6 @@
     fun testVisibleOnKeyguardOrFullScreenUserSwitcher() {
         testStateVisibility(StatusBarState.SHADE, GONE)
         testStateVisibility(StatusBarState.SHADE_LOCKED, GONE)
-        testStateVisibility(StatusBarState.FULLSCREEN_USER_SWITCHER, VISIBLE)
         testStateVisibility(StatusBarState.KEYGUARD, VISIBLE)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 140a395..609291a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -125,8 +125,9 @@
     private lateinit var settings: View
     private lateinit var settingsText: TextView
     private lateinit var cancel: View
+    private lateinit var cancelText: TextView
     private lateinit var dismiss: FrameLayout
-    private lateinit var dismissLabel: View
+    private lateinit var dismissText: TextView
 
     private lateinit var session: MediaSession
     private val device = MediaDeviceData(true, null, DEVICE_NAME)
@@ -163,8 +164,9 @@
         settings = View(context)
         settingsText = TextView(context)
         cancel = View(context)
+        cancelText = TextView(context)
         dismiss = FrameLayout(context)
-        dismissLabel = View(context)
+        dismissText = TextView(context)
         initPlayerHolderMocks()
         initSessionHolderMocks()
 
@@ -244,13 +246,15 @@
         whenever(holder.settings).thenReturn(settings)
         whenever(holder.settingsText).thenReturn(settingsText)
         whenever(holder.cancel).thenReturn(cancel)
+        whenever(holder.cancelText).thenReturn(cancelText)
         whenever(holder.dismiss).thenReturn(dismiss)
-        whenever(holder.dismissLabel).thenReturn(dismissLabel)
+        whenever(holder.dismissText).thenReturn(dismissText)
     }
 
     /** Mock view holder for session player */
     private fun initSessionHolderMocks() {
         whenever(sessionHolder.player).thenReturn(view)
+        whenever(sessionHolder.albumView).thenReturn(albumView)
         whenever(sessionHolder.appIcon).thenReturn(appIcon)
         whenever(sessionHolder.titleText).thenReturn(titleText)
         whenever(sessionHolder.artistText).thenReturn(artistText)
@@ -284,8 +288,9 @@
         whenever(sessionHolder.settings).thenReturn(settings)
         whenever(sessionHolder.settingsText).thenReturn(settingsText)
         whenever(sessionHolder.cancel).thenReturn(cancel)
+        whenever(sessionHolder.cancelText).thenReturn(cancelText)
         whenever(sessionHolder.dismiss).thenReturn(dismiss)
-        whenever(sessionHolder.dismissLabel).thenReturn(dismissLabel)
+        whenever(sessionHolder.dismissText).thenReturn(dismissText)
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt
new file mode 100644
index 0000000..c261086
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt
@@ -0,0 +1,124 @@
+package com.android.systemui.media.nearby
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.media.INearbyMediaDevicesProvider
+import com.android.systemui.shared.media.INearbyMediaDevicesService
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_LONG
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
+import com.android.systemui.shared.media.NearbyDevice
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class NearbyMediaDevicesServiceTest : SysuiTestCase() {
+
+    private lateinit var service: NearbyMediaDevicesService
+    private lateinit var binderInterface: INearbyMediaDevicesService
+
+    @Before
+    fun setUp() {
+        service = NearbyMediaDevicesService()
+        binderInterface = INearbyMediaDevicesService.Stub.asInterface(service.onBind(null))
+    }
+
+    @Test
+    fun getCurrentNearbyDevices_noProviderRegistered_returnsEmptyList() {
+        assertThat(service.getCurrentNearbyDevices()).isEmpty()
+    }
+
+    @Test
+    fun getCurrentNearbyDevices_providerRegistered_returnsProviderInfo() {
+        val nearbyDevice1 = NearbyDevice("routeId1", RANGE_LONG)
+        val nearbyDevice2 = NearbyDevice("routeId2", RANGE_WITHIN_REACH)
+        val provider = object : INearbyMediaDevicesProvider.Stub() {
+            override fun getCurrentNearbyDevices(): List<NearbyDevice> {
+                return listOf(nearbyDevice1, nearbyDevice2)
+            }
+
+            override fun registerNearbyDevicesCallback(
+                callback: INearbyMediaDevicesUpdateCallback?
+            ) {}
+            override fun unregisterNearbyDevicesCallback(
+                callback: INearbyMediaDevicesUpdateCallback?
+            ) {}
+        }
+        binderInterface.registerProvider(provider)
+
+        val returnedNearbyDevices = service.getCurrentNearbyDevices()
+
+        assertThat(returnedNearbyDevices).isEqualTo(listOf(nearbyDevice1, nearbyDevice2))
+    }
+
+    @Test
+    fun registerNearbyDevicesCallback_noProviderRegistered_noCrash() {
+        // No assert, just needs no crash
+        service.registerNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
+            override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+        })
+    }
+
+    @Test
+    fun registerNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
+        val provider = object : INearbyMediaDevicesProvider.Stub() {
+            var registeredCallback: INearbyMediaDevicesUpdateCallback? = null
+            override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
+
+            override fun registerNearbyDevicesCallback(
+                callback: INearbyMediaDevicesUpdateCallback?
+            ) {
+                registeredCallback = callback
+            }
+
+            override fun unregisterNearbyDevicesCallback(
+                callback: INearbyMediaDevicesUpdateCallback?
+            ) {}
+        }
+        binderInterface.registerProvider(provider)
+
+        val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
+            override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+        }
+
+        service.registerNearbyDevicesCallback(callback)
+
+        assertThat(provider.registeredCallback).isEqualTo(callback)
+    }
+
+    @Test
+    fun unregisterNearbyDevicesCallback_noProviderRegistered_noCrash() {
+        // No assert, just needs no crash
+        service.unregisterNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
+            override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+        })
+    }
+
+    @Test
+    fun unregisterNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
+        val provider = object : INearbyMediaDevicesProvider.Stub() {
+            var unregisteredCallback: INearbyMediaDevicesUpdateCallback? = null
+            override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
+
+            override fun registerNearbyDevicesCallback(
+                callback: INearbyMediaDevicesUpdateCallback?
+            ) {}
+
+            override fun unregisterNearbyDevicesCallback(
+                callback: INearbyMediaDevicesUpdateCallback?
+            ) {
+                unregisteredCallback = callback
+            }
+        }
+        binderInterface.registerProvider(provider)
+
+        val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
+            override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+        }
+
+        service.unregisterNearbyDevicesCallback(callback)
+
+        assertThat(provider.unregisteredCallback).isEqualTo(callback)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 81ae209..5f800eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -21,9 +21,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderService
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.util.mockito.any
@@ -54,19 +51,10 @@
 
     @Mock
     private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
-    @Mock
-    private lateinit var mediaSenderService: IDeviceSenderService.Stub
-    private lateinit var mediaSenderServiceComponentName: ComponentName
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
-        mediaSenderServiceComponentName = ComponentName(context, MediaTttSenderService::class.java)
-        context.addMockService(mediaSenderServiceComponentName, mediaSenderService)
-        whenever(mediaSenderService.queryLocalInterface(anyString())).thenReturn(mediaSenderService)
-        whenever(mediaSenderService.asBinder()).thenReturn(mediaSenderService)
-
         mediaTttCommandLineHelper =
             MediaTttCommandLineHelper(
                 commandRegistry,
@@ -100,6 +88,7 @@
         ) { EmptyCommand() }
     }
 
+    /* TODO(b/216318437): Revive these tests using the new SystemApis.
     @Test
     fun sender_moveCloserToStartCast_serviceCallbackCalled() {
         commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand())
@@ -182,6 +171,8 @@
         verify(mediaSenderService).noLongerCloseToReceiver(any(), any())
     }
 
+     */
+
     @Test
     fun receiver_addCommand_chipAdded() {
         commandRegistry.onShellCommand(pw, arrayOf(ADD_CHIP_COMMAND_RECEIVER_TAG))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 6b4eebe..58f4818 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -24,9 +24,10 @@
 import android.widget.LinearLayout
 import android.widget.TextView
 import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -46,12 +47,14 @@
 
     @Mock
     private lateinit var windowManager: WindowManager
+    @Mock
+    private lateinit var commandQueue: CommandQueue
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
-        controllerSender = MediaTttChipControllerSender(context, windowManager)
+        controllerSender = MediaTttChipControllerSender(context, windowManager, commandQueue)
     }
 
     @Test
@@ -133,7 +136,7 @@
 
     @Test
     fun transferToReceiverSucceeded_withUndoRunnable_undoWithClick() {
-        val undoCallback = object : IUndoTransferCallback.Stub() {
+        val undoCallback = object : IUndoMediaTransferCallback.Stub() {
             override fun onUndoTriggered() {}
         }
         controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
@@ -146,7 +149,7 @@
     @Test
     fun transferToReceiverSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
         var undoCallbackCalled = false
-        val undoCallback = object : IUndoTransferCallback.Stub() {
+        val undoCallback = object : IUndoMediaTransferCallback.Stub() {
             override fun onUndoTriggered() {
                 undoCallbackCalled = true
             }
@@ -160,7 +163,7 @@
 
     @Test
     fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
-        val undoCallback = object : IUndoTransferCallback.Stub() {
+        val undoCallback = object : IUndoMediaTransferCallback.Stub() {
             override fun onUndoTriggered() {}
         }
         controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
@@ -194,7 +197,7 @@
 
     @Test
     fun transferToThisDeviceSucceeded_withUndoRunnable_undoWithClick() {
-        val undoCallback = object : IUndoTransferCallback.Stub() {
+        val undoCallback = object : IUndoMediaTransferCallback.Stub() {
             override fun onUndoTriggered() {}
         }
         controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
@@ -207,7 +210,7 @@
     @Test
     fun transferToThisDeviceSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
         var undoCallbackCalled = false
-        val undoCallback = object : IUndoTransferCallback.Stub() {
+        val undoCallback = object : IUndoMediaTransferCallback.Stub() {
             override fun onUndoTriggered() {
                 undoCallbackCalled = true
             }
@@ -221,7 +224,7 @@
 
     @Test
     fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToReceiverTriggered() {
-        val undoCallback = object : IUndoTransferCallback.Stub() {
+        val undoCallback = object : IUndoMediaTransferCallback.Stub() {
             override fun onUndoTriggered() {}
         }
         controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
@@ -267,7 +270,7 @@
         controllerSender.displayChip(transferToReceiverTriggered())
         controllerSender.displayChip(
             transferToReceiverSucceeded(
-                object : IUndoTransferCallback.Stub() {
+                object : IUndoMediaTransferCallback.Stub() {
                     override fun onUndoTriggered() {}
                 }
             )
@@ -327,13 +330,13 @@
         TransferToThisDeviceTriggered(appIconDrawable, APP_ICON_CONTENT_DESC)
 
     /** Helper method providing default parameters to not clutter up the tests. */
-    private fun transferToReceiverSucceeded(undoCallback: IUndoTransferCallback? = null) =
+    private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToReceiverSucceeded(
             appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
-    private fun transferToThisDeviceSucceeded(undoCallback: IUndoTransferCallback? = null) =
+    private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToThisDeviceSucceeded(
             appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
deleted file mode 100644
index 64542cb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.android.systemui.media.taptotransfer.sender
-
-import android.media.MediaRoute2Info
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderService
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.capture
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@Ignore("b/216286227")
-class MediaTttSenderServiceTest : SysuiTestCase() {
-
-    private lateinit var service: IDeviceSenderService
-
-    @Mock
-    private lateinit var controller: MediaTttChipControllerSender
-
-    private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
-        .addFeature("feature")
-        .build()
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        val mediaTttSenderService = MediaTttSenderService(context, controller)
-        service = IDeviceSenderService.Stub.asInterface(mediaTttSenderService.onBind(null))
-    }
-
-    @Test
-    fun closeToReceiverToStartCast_controllerTriggeredWithCorrectState() {
-        val name = "Fake name"
-        service.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name))
-
-        val chipStateCaptor = argumentCaptor<MoveCloserToStartCast>()
-        verify(controller).displayChip(capture(chipStateCaptor))
-
-        val chipState = chipStateCaptor.value!!
-        assertThat(chipState.getChipTextString(context)).contains(name)
-    }
-
-    @Test
-    fun closeToReceiverToEndCast_controllerTriggeredWithCorrectState() {
-        val name = "Fake name"
-        service.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name))
-
-        val chipStateCaptor = argumentCaptor<MoveCloserToEndCast>()
-        verify(controller).displayChip(capture(chipStateCaptor))
-
-        val chipState = chipStateCaptor.value!!
-        assertThat(chipState.getChipTextString(context)).contains(name)
-    }
-
-    @Test
-    fun transferToThisDeviceTriggered_controllerTriggeredWithCorrectState() {
-        service.transferToThisDeviceTriggered(mediaInfo, DeviceInfo("Fake name"))
-
-        verify(controller).displayChip(any<TransferToThisDeviceTriggered>())
-    }
-
-    @Test
-    fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() {
-        val name = "Fake name"
-        service.transferToReceiverTriggered(mediaInfo, DeviceInfo(name))
-
-        val chipStateCaptor = argumentCaptor<TransferToReceiverTriggered>()
-        verify(controller).displayChip(capture(chipStateCaptor))
-
-        val chipState = chipStateCaptor.value!!
-        assertThat(chipState.getChipTextString(context)).contains(name)
-    }
-
-    @Test
-    fun transferToReceiverSucceeded_controllerTriggeredWithCorrectState() {
-        val name = "Fake name"
-        val undoCallback = object : IUndoTransferCallback.Stub() {
-            override fun onUndoTriggered() {}
-        }
-        service.transferToReceiverSucceeded(mediaInfo, DeviceInfo(name), undoCallback)
-
-        val chipStateCaptor = argumentCaptor<TransferToReceiverSucceeded>()
-        verify(controller).displayChip(capture(chipStateCaptor))
-
-        val chipState = chipStateCaptor.value!!
-        assertThat(chipState.getChipTextString(context)).contains(name)
-        assertThat(chipState.undoCallback).isEqualTo(undoCallback)
-    }
-
-    @Test
-    fun transferToThisDeviceSucceeded_controllerTriggeredWithCorrectState() {
-        val undoCallback = object : IUndoTransferCallback.Stub() {
-            override fun onUndoTriggered() {}
-        }
-        service.transferToThisDeviceSucceeded(mediaInfo, DeviceInfo("name"), undoCallback)
-
-        val chipStateCaptor = argumentCaptor<TransferToThisDeviceSucceeded>()
-        verify(controller).displayChip(capture(chipStateCaptor))
-
-        val chipState = chipStateCaptor.value!!
-        assertThat(chipState.undoCallback).isEqualTo(undoCallback)
-    }
-
-    @Test
-    fun transferFailed_controllerTriggeredWithTransferFailedState() {
-        service.transferFailed(mediaInfo, DeviceInfo("Fake name"))
-
-        verify(controller).displayChip(any<TransferFailed>())
-    }
-
-    @Test
-    fun noLongerCloseToReceiver_controllerRemoveChipTriggered() {
-        service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name"))
-
-        verify(controller).removeChip()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 354bb51..f5fa0d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -15,9 +15,10 @@
 import com.android.systemui.Dependency
 import com.android.systemui.R
 import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.FooterActionsController.ExpansionState
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.MultiUserSwitchController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -54,12 +55,16 @@
     @Mock
     private lateinit var userInfoController: UserInfoController
     @Mock
+    private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
+    @Mock
     private lateinit var multiUserSwitchController: MultiUserSwitchController
     @Mock
     private lateinit var globalActionsDialog: GlobalActionsDialogLite
     @Mock
     private lateinit var uiEventLogger: UiEventLogger
     @Mock
+    private lateinit var featureFlags: FeatureFlags
+
     private lateinit var controller: FooterActionsController
 
     private val metricsLogger: MetricsLogger = FakeMetricsLogger()
@@ -76,15 +81,18 @@
         injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
         val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
 
+        whenever(multiUserSwitchControllerFactory.create(any()))
+                .thenReturn(multiUserSwitchController)
+        whenever(featureFlags.isEnabled(Flags.NEW_FOOTER)).thenReturn(false)
+
         view = LayoutInflater.from(context)
                 .inflate(R.layout.footer_actions, null) as FooterActionsView
 
-        controller = FooterActionsController(view, activityStarter,
-                userManager, userTracker, userInfoController, multiUserSwitchController,
+        controller = FooterActionsController(view, multiUserSwitchControllerFactory,
+                activityStarter, userManager, userTracker, userInfoController,
                 deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
-                globalActionsDialog, uiEventLogger, showPMLiteButton = true,
-                buttonsVisibleState = ExpansionState.EXPANDED, fakeSettings,
-                Handler(testableLooper.looper))
+                globalActionsDialog, uiEventLogger, showPMLiteButton = true, fakeSettings,
+                Handler(testableLooper.looper), featureFlags)
         controller.init()
         ViewUtils.attachView(view)
         // View looper is the testable looper associated with the test
@@ -98,7 +106,7 @@
 
     @Test
     fun testLogPowerMenuClick() {
-        controller.expanded = true
+        controller.visible = true
         falsingManager.setFalseTap(false)
 
         view.findViewById<View>(R.id.pm_lite).performClick()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index f43e68f..26aa37d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -61,12 +61,8 @@
     @Mock
     private ClipboardManager mClipboardManager;
     @Mock
-    private QuickQSPanelController mQuickQSPanelController;
-    @Mock
     private TextView mBuildText;
     @Mock
-    private FooterActionsController mFooterActionsController;
-    @Mock
     private FalsingManager mFalsingManager;
     @Mock
     private ActivityStarter mActivityStarter;
@@ -93,8 +89,7 @@
         when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton);
 
         mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager,
-                mActivityStarter, mQSPanelController, mQuickQSPanelController,
-                mFooterActionsController);
+                mActivityStarter, mQSPanelController);
 
         mController.init();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 59948d3..09c6d9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -68,8 +68,6 @@
     private lateinit var tileView: QSTileView
     @Mock
     private lateinit var quickQsBrightnessController: QuickQSBrightnessController
-    @Mock
-    private lateinit var footerActionsController: FooterActionsController
     @Captor
     private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
 
@@ -95,8 +93,7 @@
                 uiEventLogger,
                 qsLogger,
                 dumpManager,
-                quickQsBrightnessController,
-                footerActionsController
+                quickQsBrightnessController
         )
 
         controller.init()
@@ -128,14 +125,12 @@
     }
 
     @Test
-    fun testBrightnessAndFooterVisibilityRefreshedWhenConfigurationChanged() {
+    fun testBrightnessRefreshedWhenConfigurationChanged() {
         // times(2) because both controller and base controller are registering their listeners
         verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
 
         captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
 
         verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
-        // times(2) because footer visibility is also refreshed on controller init
-        verify(footerActionsController, times(2)).refreshVisibility(anyBoolean())
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index d51d370..9076e16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -169,8 +169,6 @@
         transitionController.goToLockedShade(null)
         whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
         transitionController.goToLockedShade(null)
-        whenever(statusbarStateController.state).thenReturn(StatusBarState.FULLSCREEN_USER_SWITCHER)
-        transitionController.goToLockedShade(null)
         verify(statusbarStateController, never()).setState(anyInt())
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index b736f38..a5ea897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -61,18 +61,16 @@
     @Test
     fun testChangeState_logged() {
         TestableLooper.get(this).runWithLooper {
-            controller.state = StatusBarState.FULLSCREEN_USER_SWITCHER
             controller.state = StatusBarState.KEYGUARD
             controller.state = StatusBarState.SHADE
             controller.state = StatusBarState.SHADE_LOCKED
         }
 
         val logs = uiEventLogger.logs
-        assertEquals(4, logs.size)
+        assertEquals(3, logs.size)
         val ids = logs.map(UiEventLoggerFake.FakeUiEvent::eventId)
-        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER.id, ids[0])
-        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[1])
-        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[2])
-        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[3])
+        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[0])
+        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[1])
+        assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[2])
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
index b5b2f1f..79a2008 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
@@ -33,18 +33,16 @@
                 StatusBarStateEvent.STATUS_BAR_STATE_SHADE,
                 StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED,
                 StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD,
-                StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER,
                 StatusBarStateEvent.STATUS_BAR_STATE_UNKNOWN
         )
         val states = listOf(
                 StatusBarState.SHADE,
                 StatusBarState.SHADE_LOCKED,
                 StatusBarState.KEYGUARD,
-                StatusBarState.FULLSCREEN_USER_SWITCHER,
                 -1
         )
         events.zip(states).forEach { (event, state) ->
             assertEquals(event, StatusBarStateEvent.fromState(state))
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index bdcbbbc..4f731ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED;
 import static android.view.View.GONE;
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -41,8 +40,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.MathUtils;
@@ -112,10 +109,6 @@
     public void setUp() throws Exception {
         allowTestableLooperAsMainThread();
 
-        Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED,
-                1, UserHandle.USER_CURRENT);
-
-
         // Interact with real instance of AmbientState.
         mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController);
 
@@ -150,6 +143,7 @@
         mStackScroller.setShelfController(notificationShelfController);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
         when(mStackScrollLayoutController.getNoticationRoundessManager())
                 .thenReturn(mNotificationRoundnessManager);
         mStackScroller.setController(mStackScrollLayoutController);
@@ -404,6 +398,22 @@
     }
 
     @Test
+    public void testUpdateFooter_withoutHistory() {
+        setBarStateForTest(StatusBarState.SHADE);
+        mStackScroller.setCurrentUserSetup(true);
+
+        when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false);
+        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
+                .thenReturn(true);
+
+        FooterView view = mock(FooterView.class);
+        mStackScroller.setFooterView(view);
+        mStackScroller.updateFooter();
+        verify(mStackScroller).updateFooterView(true, true, false);
+    }
+
+    @Test
     public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 8c7d22d..fb232ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -38,6 +38,7 @@
 import android.testing.TestableResources;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
@@ -110,6 +111,8 @@
     private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     @Mock
     private SessionTracker mSessionTracker;
+    @Mock
+    private LatencyTracker mLatencyTracker;
     private BiometricUnlockController mBiometricUnlockController;
 
     @Before
@@ -133,7 +136,7 @@
                 mMetricsLogger, mDumpManager, mPowerManager,
                 mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
                 mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
-                mSessionTracker);
+                mSessionTracker, mLatencyTracker);
         mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index 77e9025..bbb2346 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -6,6 +6,8 @@
 import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
 import com.android.systemui.recents.OverviewProxyService
@@ -46,6 +48,8 @@
     private lateinit var overviewProxyService: OverviewProxyService
     @Mock
     private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
+    @Mock
+    private lateinit var featureFlags: FeatureFlags
     @Captor
     lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
     @Captor
@@ -64,7 +68,8 @@
         notificationsQSContainerController = NotificationsQSContainerController(
                 notificationsQSContainer,
                 navigationModeController,
-                overviewProxyService
+                overviewProxyService,
+                featureFlags
         )
         whenever(notificationsQSContainer.defaultNotificationsMarginBottom)
                 .thenReturn(NOTIFICATIONS_MARGIN)
@@ -85,6 +90,26 @@
     @Test
     fun testTaskbarVisibleInSplitShade() {
         notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(false)
+
+        given(taskbarVisible = true,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
+                expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+
+        given(taskbarVisible = true,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = STABLE_INSET_BOTTOM,
+                expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+    }
+
+    @Test
+    fun testTaskbarVisibleInSplitShade_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(true)
+
         given(taskbarVisible = true,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = windowInsets().withStableBottom())
@@ -102,6 +127,26 @@
     fun testTaskbarNotVisibleInSplitShade() {
         // when taskbar is not visible, it means we're on the home screen
         notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(false)
+
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0)
+
+        given(taskbarVisible = false,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
+                expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+    }
+
+    @Test
+    fun testTaskbarNotVisibleInSplitShade_newFooter() {
+        // when taskbar is not visible, it means we're on the home screen
+        notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(true)
+
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = windowInsets().withStableBottom())
@@ -117,6 +162,25 @@
     @Test
     fun testTaskbarNotVisibleInSplitShadeWithCutout() {
         notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(false)
+
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withCutout())
+        then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+        given(taskbarVisible = false,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = windowInsets().withCutout().withStableBottom())
+        then(expectedContainerPadding = 0,
+                expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+    }
+
+    @Test
+    fun testTaskbarNotVisibleInSplitShadeWithCutout_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(true)
+
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = windowInsets().withCutout())
@@ -132,6 +196,24 @@
     @Test
     fun testTaskbarVisibleInSinglePaneShade() {
         notificationsQSContainerController.splitShadeEnabled = false
+        useNewFooter(false)
+
+        given(taskbarVisible = true,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0)
+
+        given(taskbarVisible = true,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = STABLE_INSET_BOTTOM)
+    }
+
+    @Test
+    fun testTaskbarVisibleInSinglePaneShade_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = false
+        useNewFooter(true)
+
         given(taskbarVisible = true,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = windowInsets().withStableBottom())
@@ -146,6 +228,8 @@
     @Test
     fun testTaskbarNotVisibleInSinglePaneShade() {
         notificationsQSContainerController.splitShadeEnabled = false
+        useNewFooter(false)
+
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = emptyInsets())
@@ -159,14 +243,56 @@
         given(taskbarVisible = false,
                 navigationMode = BUTTONS_NAVIGATION,
                 insets = windowInsets().withStableBottom())
-        then(expectedContainerPadding = 0,
-                expectedQsPadding = STABLE_INSET_BOTTOM)
+        then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM)
+    }
+
+    @Test
+    fun testTaskbarNotVisibleInSinglePaneShade_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = false
+        useNewFooter(true)
+
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = emptyInsets())
+        then(expectedContainerPadding = 0)
+
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withCutout().withStableBottom())
+        then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+        given(taskbarVisible = false,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM)
     }
 
     @Test
     fun testCustomizingInSinglePaneShade() {
         notificationsQSContainerController.splitShadeEnabled = false
         notificationsQSContainerController.setCustomizerShowing(true)
+        useNewFooter(false)
+
+        // always sets spacings to 0
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0,
+                expectedNotificationsMargin = 0)
+
+        given(taskbarVisible = false,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = emptyInsets())
+        then(expectedContainerPadding = 0,
+                expectedNotificationsMargin = 0)
+    }
+
+    @Test
+    fun testCustomizingInSinglePaneShade_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = false
+        notificationsQSContainerController.setCustomizerShowing(true)
+        useNewFooter(true)
+
         // always sets spacings to 0
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
@@ -185,6 +311,28 @@
     fun testDetailShowingInSinglePaneShade() {
         notificationsQSContainerController.splitShadeEnabled = false
         notificationsQSContainerController.setDetailShowing(true)
+        useNewFooter(false)
+
+        // always sets spacings to 0
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0,
+                expectedNotificationsMargin = 0)
+
+        given(taskbarVisible = false,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = emptyInsets())
+        then(expectedContainerPadding = 0,
+                expectedNotificationsMargin = 0)
+    }
+
+    @Test
+    fun testDetailShowingInSinglePaneShade_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = false
+        notificationsQSContainerController.setDetailShowing(true)
+        useNewFooter(true)
+
         // always sets spacings to 0
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
@@ -202,6 +350,26 @@
     @Test
     fun testDetailShowingInSplitShade() {
         notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(false)
+
+        given(taskbarVisible = false,
+                navigationMode = GESTURES_NAVIGATION,
+                insets = windowInsets().withStableBottom())
+        then(expectedContainerPadding = 0)
+
+        notificationsQSContainerController.setDetailShowing(true)
+        // should not influence spacing
+        given(taskbarVisible = false,
+                navigationMode = BUTTONS_NAVIGATION,
+                insets = emptyInsets())
+        then(expectedContainerPadding = 0)
+    }
+
+    @Test
+    fun testDetailShowingInSplitShade_newFooter() {
+        notificationsQSContainerController.splitShadeEnabled = true
+        useNewFooter(true)
+
         given(taskbarVisible = false,
                 navigationMode = GESTURES_NAVIGATION,
                 insets = windowInsets().withStableBottom())
@@ -246,7 +414,13 @@
         verify(notificationsQSContainer)
                 .setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
         verify(notificationsQSContainer).setNotificationsMarginBottom(expectedNotificationsMargin)
-        verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
+        val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
+        if (newFooter) {
+            verify(notificationsQSContainer)
+                    .setQSContainerPaddingBottom(expectedNotificationsMargin)
+        } else {
+            verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
+        }
         Mockito.clearInvocations(notificationsQSContainer)
     }
 
@@ -263,4 +437,8 @@
         whenever(stableInsetBottom).thenReturn(STABLE_INSET_BOTTOM)
         return this
     }
+
+    private fun useNewFooter(useNewFooter: Boolean) {
+        whenever(featureFlags.isEnabled(Flags.NEW_FOOTER)).thenReturn(useNewFooter)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bb79941..107ba81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -36,6 +36,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardMessageArea;
 import com.android.keyguard.KeyguardMessageAreaController;
@@ -100,6 +101,8 @@
     private ShadeController mShadeController;
     @Mock
     private DreamOverlayStateController mDreamOverlayStateController;
+    @Mock
+    private LatencyTracker mLatencyTracker;
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
@@ -127,7 +130,8 @@
                 mock(NotificationMediaManager.class),
                 mKeyguardBouncerFactory,
                 mKeyguardMessageAreaFactory,
-                () -> mShadeController);
+                () -> mShadeController,
+                mLatencyTracker);
         mStatusBarKeyguardViewManager.registerStatusBar(
                 mStatusBar,
                 mNotificationPanelView,
@@ -171,17 +175,6 @@
     }
 
     @Test
-    public void onPanelExpansionChanged_neverHidesFullscreenBouncer() {
-        // TODO: StatusBar should not be here, mBouncer.isFullscreenBouncer() should do the same.
-        when(mStatusBar.isFullScreenUserSwitcherState()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                /* fraction= */ 0.5f,
-                /* expanded= */ false,
-                /* tracking= */ true);
-        verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
-    }
-
-    @Test
     public void onPanelExpansionChanged_neverHidesScrimmedBouncer() {
         when(mBouncer.isShowing()).thenReturn(true);
         when(mBouncer.isScrimmed()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 1564dfe..90b93e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -885,15 +885,6 @@
     }
 
     @Test
-    public void testSetState_changesIsFullScreenUserSwitcherState() {
-        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
-        assertFalse(mStatusBar.isFullScreenUserSwitcherState());
-
-        mStatusBar.setBarStateForTest(StatusBarState.FULLSCREEN_USER_SWITCHER);
-        assertTrue(mStatusBar.isFullScreenUserSwitcherState());
-    }
-
-    @Test
     public void testShowKeyguardImplementation_setsState() {
         when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
 
@@ -903,12 +894,6 @@
         mStatusBar.showKeyguardImpl();
         verify(mStatusBarStateController).setState(
                 eq(StatusBarState.KEYGUARD), eq(false) /* force */);
-
-        // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
-        when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
-        mStatusBar.showKeyguardImpl();
-        verify(mStatusBarStateController).setState(
-                eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */);
     }
 
     @Test
diff --git a/services/Android.bp b/services/Android.bp
index af70692..b0a5c66 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -102,6 +102,7 @@
         ":services.usage-sources",
         ":services.usb-sources",
         ":services.voiceinteraction-sources",
+        ":services.wallpapereffectsgeneration-sources",
         ":services.wifi-sources",
     ],
     visibility: ["//visibility:private"],
@@ -158,6 +159,7 @@
         "services.usage",
         "services.usb",
         "services.voiceinteraction",
+        "services.wallpapereffectsgeneration",
         "services.wifi",
         "service-blobstore",
         "service-jobscheduler",
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 6744ea8..803177b 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -295,6 +295,21 @@
                 case AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK :
                     sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
                     return true;
+                case AccessibilityService.GLOBAL_ACTION_DPAD_UP:
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP);
+                    return true;
+                case AccessibilityService.GLOBAL_ACTION_DPAD_DOWN:
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN);
+                    return true;
+                case AccessibilityService.GLOBAL_ACTION_DPAD_LEFT:
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
+                    return true;
+                case AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT:
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
+                    return true;
+                case AccessibilityService.GLOBAL_ACTION_DPAD_CENTER:
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
+                    return true;
                 default:
                     Slog.e(TAG, "Invalid action id: " + actionId);
                     return false;
diff --git a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
index f1d98f0..0509e0c 100644
--- a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
+++ b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
@@ -36,7 +36,6 @@
  * will be killed if association/role are revoked.
  */
 public class AssociationCleanUpService extends JobService {
-    private static final String TAG = LOG_TAG + ".AssociationCleanUpService";
     private static final int JOB_ID = AssociationCleanUpService.class.hashCode();
     private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day
     private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService(
@@ -56,7 +55,7 @@
 
     @Override
     public boolean onStopJob(final JobParameters params) {
-        Slog.d(TAG, "Association cleanup job stopped; id=" + params.getJobId()
+        Slog.i(LOG_TAG, "Association cleanup job stopped; id=" + params.getJobId()
                 + ", reason="
                 + JobParameters.getInternalReasonCodeDescription(
                 params.getInternalStopReasonCode()));
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 3ccabaa..21a677b8 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.companion.AssociationInfo;
 import android.net.MacAddress;
@@ -52,9 +53,10 @@
  * Other system component (both inside and outside if the com.android.server.companion package)
  * should use public {@link AssociationStore} interface.
  */
+@SuppressLint("LongLogTag")
 class AssociationStoreImpl implements AssociationStore {
     private static final boolean DEBUG = false;
-    private static final String TAG = "AssociationStore";
+    private static final String TAG = "CompanionDevice_AssociationStore";
 
     private final Object mLock = new Object();
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index f2e66077..fd13085 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -16,16 +16,17 @@
 
 package com.android.server.companion;
 
-import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
-
 import android.companion.AssociationInfo;
+import android.os.ShellCommand;
 import android.util.Log;
 import android.util.Slog;
 
 import java.io.PrintWriter;
 import java.util.List;
 
-class CompanionDeviceShellCommand extends android.os.ShellCommand {
+class CompanionDeviceShellCommand extends ShellCommand {
+    private static final String TAG = "CompanionDevice_ShellCommand";
+
     private final CompanionDeviceManagerService mService;
     private final AssociationStore mAssociationStore;
 
@@ -84,7 +85,7 @@
             }
             return 0;
         } catch (Throwable t) {
-            Slog.e(LOG_TAG, "Error running a command: $ " + cmd, t);
+            Slog.e(TAG, "Error running a command: $ " + cmd, t);
             getErrPrintWriter().println(Log.getStackTraceString(t));
             return 1;
         }
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
index 6055a81..8ac741a 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -25,7 +25,7 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 
-import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -34,8 +34,7 @@
 import java.io.FileOutputStream;
 
 final class DataStoreUtils {
-
-    private static final String LOG_TAG = DataStoreUtils.class.getSimpleName();
+    private static final String TAG = "CompanionDevice_DataStoreUtils";
 
     static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
             throws XmlPullParserException {
@@ -71,12 +70,12 @@
      * Writing to file could fail, for example, if the user has been recently removed and so was
      * their DE (/data/system_de/<user-id>/) directory.
      */
-    static void writeToFileSafely(@NonNull AtomicFile file,
-            @NonNull FunctionalUtils.ThrowingConsumer<FileOutputStream> consumer) {
+    static void writeToFileSafely(
+            @NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) {
         try {
             file.write(consumer);
         } catch (Exception e) {
-            Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+            Slog.e(TAG, "Error while writing to file " + file, e);
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index da33b44..d0cc122 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -32,6 +32,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.companion.AssociationInfo;
 import android.content.pm.UserInfo;
@@ -39,6 +40,7 @@
 import android.os.Environment;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedXmlPullParser;
@@ -146,8 +148,9 @@
  * </state>
  * }</pre>
  */
+@SuppressLint("LongLogTag")
 final class PersistentDataStore {
-    private static final String LOG_TAG = CompanionDeviceManagerService.LOG_TAG + ".DataStore";
+    private static final String TAG = "CompanionDevice_PersistentDataStore";
     private static final boolean DEBUG = CompanionDeviceManagerService.DEBUG;
 
     private static final int CURRENT_PERSISTENCE_VERSION = 1;
@@ -208,10 +211,9 @@
     void readStateForUser(@UserIdInt int userId,
             @NonNull Collection<AssociationInfo> associationsOut,
             @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
-        Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
-
+        Slog.i(TAG, "Reading associations for user " + userId + " from disk");
         final AtomicFile file = getStorageFileForUser(userId);
-        if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
+        if (DEBUG) Log.d(TAG, "  > File=" + file.getBaseFile().getPath());
 
         // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
         // accesses to the file on the file system using this AtomicFile object.
@@ -220,12 +222,12 @@
             final AtomicFile readFrom;
             final String rootTag;
             if (!file.getBaseFile().exists()) {
-                if (DEBUG) Slog.d(LOG_TAG, "  > File does not exist -> Try to read legacy file");
+                if (DEBUG) Log.d(TAG, "  > File does not exist -> Try to read legacy file");
 
                 legacyBaseFile = getBaseLegacyStorageFileForUser(userId);
-                if (DEBUG) Slog.d(LOG_TAG, "  > Legacy file=" + legacyBaseFile.getPath());
+                if (DEBUG) Log.d(TAG, "  > Legacy file=" + legacyBaseFile.getPath());
                 if (!legacyBaseFile.exists()) {
-                    if (DEBUG) Slog.d(LOG_TAG, "  > Legacy file does not exist -> Abort");
+                    if (DEBUG) Log.d(TAG, "  > Legacy file does not exist -> Abort");
                     return;
                 }
 
@@ -236,13 +238,13 @@
                 rootTag = XML_TAG_STATE;
             }
 
-            if (DEBUG) Slog.d(LOG_TAG, "  > Reading associations...");
+            if (DEBUG) Log.d(TAG, "  > Reading associations...");
             final int version = readStateFromFileLocked(userId, readFrom, rootTag,
                     associationsOut, previouslyUsedIdsPerPackageOut);
             if (DEBUG) {
-                Slog.d(LOG_TAG, "  > Done reading: " + associationsOut);
+                Log.d(TAG, "  > Done reading: " + associationsOut);
                 if (version < CURRENT_PERSISTENCE_VERSION) {
-                    Slog.d(LOG_TAG, "  > File used old format: v." + version + " -> Re-write");
+                    Log.d(TAG, "  > File used old format: v." + version + " -> Re-write");
                 }
             }
 
@@ -250,13 +252,13 @@
                 // The data is either in the legacy file or in the legacy format, or both.
                 // Save the data to right file in using the current format.
                 if (DEBUG) {
-                    Slog.d(LOG_TAG, "  > Writing the data to " + file.getBaseFile().getPath());
+                    Log.d(TAG, "  > Writing the data to " + file.getBaseFile().getPath());
                 }
                 persistStateToFileLocked(file, associationsOut, previouslyUsedIdsPerPackageOut);
 
                 if (legacyBaseFile != null) {
                     // We saved the data to the right file, can delete the old file now.
-                    if (DEBUG) Slog.d(LOG_TAG, "  > Deleting legacy file");
+                    if (DEBUG) Log.d(TAG, "  > Deleting legacy file");
                     legacyBaseFile.delete();
                 }
             }
@@ -273,11 +275,11 @@
     void persistStateForUser(@UserIdInt int userId,
             @NonNull Collection<AssociationInfo> associations,
             @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
-        Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
-        if (DEBUG) Slog.d(LOG_TAG, "  > " + associations);
+        Slog.i(TAG, "Writing associations for user " + userId + " to disk");
+        if (DEBUG) Slog.d(TAG, "  > " + associations);
 
         final AtomicFile file = getStorageFileForUser(userId);
-        if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
+        if (DEBUG) Log.d(TAG, "  > File=" + file.getBaseFile().getPath());
         // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
         // accesses to the file on the file system using this AtomicFile object.
         synchronized (file) {
@@ -312,7 +314,7 @@
             }
             return version;
         } catch (XmlPullParserException | IOException e) {
-            Slog.e(LOG_TAG, "Error while reading associations file", e);
+            Slog.e(TAG, "Error while reading associations file", e);
             return -1;
         }
     }
@@ -528,7 +530,7 @@
             associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
                     displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected);
         } catch (Exception e) {
-            if (DEBUG) Slog.w(LOG_TAG, "Could not create AssociationInfo", e);
+            if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
         }
         return associationInfo;
     }
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
index 76340fc..904283f 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -19,20 +19,23 @@
 import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
 
 import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.role.RoleManager;
 import android.companion.AssociationInfo;
 import android.content.Context;
 import android.os.UserHandle;
+import android.util.Log;
 import android.util.Slog;
 
 import java.util.List;
 
 /** Utility methods for accessing {@link RoleManager} APIs. */
+@SuppressLint("LongLogTag")
 final class RolesUtils {
+    private static final String TAG = CompanionDeviceManagerService.LOG_TAG;
 
     static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
             @NonNull String packageName, @NonNull String role) {
@@ -45,7 +48,7 @@
     static void addRoleHolderForAssociation(
             @NonNull Context context, @NonNull AssociationInfo associationInfo) {
         if (DEBUG) {
-            Slog.d(LOG_TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
+            Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
         }
 
         final String deviceProfile = associationInfo.getDeviceProfile();
@@ -61,7 +64,7 @@
                 MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
                 success -> {
                     if (!success) {
-                        Slog.e(LOG_TAG, "Failed to add u" + userId + "\\" + packageName
+                        Slog.e(TAG, "Failed to add u" + userId + "\\" + packageName
                                 + " to the list of " + deviceProfile + " holders.");
                     }
                 });
@@ -70,7 +73,7 @@
     static void removeRoleHolderForAssociation(
             @NonNull Context context, @NonNull AssociationInfo associationInfo) {
         if (DEBUG) {
-            Slog.d(LOG_TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
+            Log.d(TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
         }
 
         final String deviceProfile = associationInfo.getDeviceProfile();
@@ -86,7 +89,7 @@
                 MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
                 success -> {
                     if (!success) {
-                        Slog.e(LOG_TAG, "Failed to remove u" + userId + "\\" + packageName
+                        Slog.e(TAG, "Failed to remove u" + userId + "\\" + packageName
                                 + " from the list of " + deviceProfile + " holders.");
                     }
                 });
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 627b0be..a771e7b 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -33,6 +33,7 @@
 import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST;
 import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
 
+import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
 import static com.android.server.companion.presence.Utils.btDeviceToString;
 
 import static java.util.Objects.requireNonNull;
@@ -70,7 +71,6 @@
 
 @SuppressLint("LongLogTag")
 class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
-    private static final boolean DEBUG = false;
     private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
 
     /**
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index 93cbe97..1ba198a 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -16,6 +16,7 @@
 
 package com.android.server.companion.presence;
 
+import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
 import static com.android.server.companion.presence.Utils.btDeviceToString;
 
 import android.annotation.NonNull;
@@ -39,7 +40,6 @@
 class BluetoothCompanionDeviceConnectionListener
         extends BluetoothAdapter.BluetoothConnectionCallback
         implements AssociationStore.OnChangeListener {
-    private static final boolean DEBUG = false;
     private static final String TAG = "CompanionDevice_PresenceMonitor_BT";
 
     interface Callback {
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
new file mode 100644
index 0000000..6371b25
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -0,0 +1,228 @@
+/*
+ * 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.companion.presence;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.companion.AssociationInfo;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.server.companion.AssociationStore;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class responsible for monitoring companion devices' "presence" status (i.e.
+ * connected/disconnected for Bluetooth devices; nearby or not for BLE devices).
+ *
+ * <p>
+ * Should only be used by
+ * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService}
+ * to which it provides the following API:
+ * <ul>
+ * <li> {@link #onSelfManagedDeviceConnected(int)}
+ * <li> {@link #onSelfManagedDeviceDisconnected(int)}
+ * <li> {@link #isDevicePresent(int)}
+ * <li> {@link Callback#onDeviceAppeared(int) Callback.onDeviceAppeared(int)}
+ * <li> {@link Callback#onDeviceDisappeared(int) Callback.onDeviceDisappeared(int)}
+ * </ul>
+ */
+@SuppressLint("LongLogTag")
+public class CompanionDevicePresenceMonitor implements AssociationStore.OnChangeListener,
+        BluetoothCompanionDeviceConnectionListener.Callback, BleCompanionDeviceScanner.Callback {
+    static final boolean DEBUG = false;
+    private static final String TAG = "CompanionDevice_PresenceMonitor";
+
+    /** Callback for notifying about changes to status of companion devices. */
+    public interface Callback {
+        /** Invoked when companion device is found nearby or connects. */
+        void onDeviceAppeared(int associationId);
+
+        /** Invoked when a companion device no longer seen nearby or disconnects. */
+        void onDeviceDisappeared(int associationId);
+    }
+
+    private final @NonNull AssociationStore mAssociationStore;
+    private final @NonNull Callback mCallback;
+    private final @NonNull BluetoothCompanionDeviceConnectionListener mBtConnectionListener;
+    private final @NonNull BleCompanionDeviceScanner mBleScanner;
+
+    // NOTE: Same association may appear in more than one of the following sets at the same time.
+    // (E.g. self-managed devices that have MAC addresses, could be reported as present by their
+    // companion applications, while at the same be connected via BT, or detected nearby by BLE
+    // scanner)
+    private final @NonNull Set<Integer> mConnectedBtDevices = new HashSet<>();
+    private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
+    private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
+
+    public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore,
+            @NonNull Callback callback) {
+        mAssociationStore = associationStore;
+        mCallback = callback;
+
+        mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(associationStore,
+                /* BluetoothCompanionDeviceConnectionListener.Callback */ this);
+        mBleScanner = new BleCompanionDeviceScanner(associationStore,
+                /* BleCompanionDeviceScanner.Callback */ this);
+    }
+
+    /** Initialize {@link CompanionDevicePresenceMonitor} */
+    public void init(Context context) {
+        if (DEBUG) Log.i(TAG, "init()");
+
+        final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (btAdapter != null) {
+            mBtConnectionListener.init(btAdapter);
+            mBleScanner.init(context, btAdapter);
+        } else {
+            Log.w(TAG, "BluetoothAdapter is NOT available.");
+        }
+
+        mAssociationStore.registerListener(this);
+    }
+
+    /**
+     * @return whether the associated companion devices is present. I.e. device is nearby (for BLE);
+     *         or devices is connected (for Bluetooth); or reported (by the application) to be
+     *         nearby (for "self-managed" associations).
+     */
+    public boolean isDevicePresent(int associationId) {
+        return mReportedSelfManagedDevices.contains(associationId)
+                || mConnectedBtDevices.contains(associationId)
+                || mNearbyBleDevices.contains(associationId);
+    }
+
+    /**
+     * Marks a "self-managed" device as connected.
+     *
+     * <p>
+     * Must ONLY be invoked by the
+     * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService}
+     * when an application invokes
+     * {@link android.companion.CompanionDeviceManager#notifyDeviceAppeared(int) notifyDeviceAppeared()}
+     */
+    public void onSelfManagedDeviceConnected(int associationId) {
+        onDevicePresent(mReportedSelfManagedDevices, associationId, "application-reported");
+    }
+
+    /**
+     * Marks a "self-managed" device as disconnected.
+     *
+     * <p>
+     * Must ONLY be invoked by the
+     * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService}
+     * when an application invokes
+     * {@link android.companion.CompanionDeviceManager#notifyDeviceDisappeared(int) notifyDeviceDisappeared()}
+     */
+    public void onSelfManagedDeviceDisconnected(int associationId) {
+        onDeviceGone(mReportedSelfManagedDevices, associationId, "application-reported");
+    }
+
+    @Override
+    public void onBluetoothCompanionDeviceConnected(int associationId) {
+        onDevicePresent(mConnectedBtDevices, associationId, /* sourceLoggingTag */ "bt");
+    }
+
+    @Override
+    public void onBluetoothCompanionDeviceDisconnected(int associationId) {
+        onDeviceGone(mConnectedBtDevices, associationId, /* sourceLoggingTag */ "bt");
+    }
+
+    @Override
+    public void onBleCompanionDeviceFound(int associationId) {
+        onDevicePresent(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
+    }
+
+    @Override
+    public void onBleCompanionDeviceLost(int associationId) {
+        onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
+    }
+
+    private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource,
+            int newDeviceAssociationId, @NonNull String sourceLoggingTag) {
+        if (DEBUG) {
+            Log.i(TAG, "onDevice_Present() id=" + newDeviceAssociationId
+                    + ", source=" + sourceLoggingTag);
+            Log.d(TAG, "  > association="
+                    + mAssociationStore.getAssociationById(newDeviceAssociationId));
+        }
+
+        final boolean alreadyPresent = isDevicePresent(newDeviceAssociationId);
+        if (DEBUG && alreadyPresent) Log.i(TAG, "Device is already present.");
+
+        final boolean added = presentDevicesForSource.add(newDeviceAssociationId);
+        if (DEBUG && !added) {
+            Log.w(TAG, "Association with id " + newDeviceAssociationId + " is ALREADY reported as "
+                    + "present by this source (" + sourceLoggingTag + ")");
+        }
+
+        if (alreadyPresent) return;
+
+        mCallback.onDeviceAppeared(newDeviceAssociationId);
+    }
+
+    private void onDeviceGone(@NonNull Set<Integer> presentDevicesForSource,
+            int goneDeviceAssociationId, @NonNull String sourceLoggingTag) {
+        if (DEBUG) {
+            Log.i(TAG, "onDevice_Gone() id=" + goneDeviceAssociationId
+                    + ", source=" + sourceLoggingTag);
+            Log.d(TAG, "  > association="
+                    + mAssociationStore.getAssociationById(goneDeviceAssociationId));
+        }
+
+        final boolean removed = presentDevicesForSource.remove(goneDeviceAssociationId);
+        if (!removed) {
+            if (DEBUG) {
+                Log.w(TAG, "Association with id " + goneDeviceAssociationId + " was NOT reported "
+                        + "as present by this source (" + sourceLoggingTag + ")");
+            }
+            return;
+        }
+
+        final boolean stillPresent = isDevicePresent(goneDeviceAssociationId);
+        if (stillPresent) {
+            if (DEBUG) Log.i(TAG, "  Device is still present.");
+            return;
+        }
+
+        mCallback.onDeviceDisappeared(goneDeviceAssociationId);
+    }
+
+    /**
+     * Implements
+     * {@link AssociationStore.OnChangeListener#onAssociationRemoved(AssociationInfo)}
+     */
+    @Override
+    public void onAssociationRemoved(@NonNull AssociationInfo association) {
+        final int id = association.getId();
+        if (DEBUG) {
+            Log.i(TAG, "onAssociationRemoved() id=" + id);
+            Log.d(TAG, "  > association=" + association);
+        }
+
+        mConnectedBtDevices.remove(id);
+        mNearbyBleDevices.remove(id);
+        mReportedSelfManagedDevices.remove(id);
+
+        // Do NOT call mCallback.onDeviceDisappeared()!
+        // CompanionDeviceManagerService will know that the association is removed, and will do
+        // what's needed.
+    }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1106fe7..561009f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -134,6 +134,7 @@
         "app-compat-annotations",
         "framework-tethering.stubs.module_lib",
         "service-permission.stubs.system_server",
+        "service-supplementalprocess.stubs.system_server",
     ],
 
     required: [
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index db510cb..7714dbc 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -33,6 +33,7 @@
 import android.util.Slog;
 
 import com.android.internal.os.IBinaryTransparencyService;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -373,6 +374,7 @@
     private void getVBMetaDigestInformation() {
         mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
         Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
+        FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
     }
 
     @NonNull
@@ -437,6 +439,13 @@
                     } else {
                         mBinaryHashes.put(packageName, sha256digest);
                     }
+
+                    if (packageInfo.isApex) {
+                        FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED,
+                                packageInfo.packageName,
+                                packageInfo.getLongVersionCode(),
+                                mBinaryHashes.get(packageInfo.packageName));
+                    }
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 Slog.e(TAG, "Could not find package with name " + packageName);
@@ -466,6 +475,8 @@
             } else {
                 mBinaryHashes.put(packageInfo.packageName, sha256digest);
             }
+            FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED, packageInfo.packageName,
+                    packageInfo.getLongVersionCode(), mBinaryHashes.get(packageInfo.packageName));
             Slog.d(TAG, String.format("Last update time for %s: %d", packageInfo.packageName,
                     packageInfo.lastUpdateTime));
             mBinaryLastUpdateTimes.put(packageInfo.packageName, packageInfo.lastUpdateTime);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 442b9de..fafe908 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -459,7 +459,7 @@
      * broadcasts
      */
     private static final boolean ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT =
-            SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", false);
+            SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", true);
 
     static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
     static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
@@ -2901,16 +2901,31 @@
         mActivityTaskManager.setPackageScreenCompatMode(packageName, mode);
     }
 
-    private boolean hasUsageStatsPermission(String callingPackage) {
+    private boolean hasUsageStatsPermission(String callingPackage, int callingUid, int callingPid) {
         final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
-                Binder.getCallingUid(), callingPackage, null, false, "", false).getOpMode();
+                callingUid, callingPackage, null, false, "", false).getOpMode();
         if (mode == AppOpsManager.MODE_DEFAULT) {
-            return checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+            return checkPermission(Manifest.permission.PACKAGE_USAGE_STATS, callingPid, callingUid)
                     == PackageManager.PERMISSION_GRANTED;
         }
         return mode == AppOpsManager.MODE_ALLOWED;
     }
 
+    private boolean hasUsageStatsPermission(String callingPackage) {
+        return hasUsageStatsPermission(callingPackage,
+                Binder.getCallingUid(), Binder.getCallingPid());
+    }
+
+    private void enforceUsageStatsPermission(String callingPackage,
+            int callingUid, int callingPid, String operation) {
+        if (!hasUsageStatsPermission(callingPackage, callingUid, callingPid)) {
+            final String errorMsg = "Permission denial for <" + operation + "> from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " which requires PACKAGE_USAGE_STATS permission";
+            throw new SecurityException(errorMsg);
+        }
+    }
+
     @Override
     public int getPackageProcessState(String packageName, String callingPackage) {
         if (!hasUsageStatsPermission(callingPackage)) {
@@ -12783,7 +12798,7 @@
                 noAction.add(null);
                 actions = noAction.iterator();
             }
-            boolean onlyProtectedBroadcasts = actions.hasNext();
+            boolean onlyProtectedBroadcasts = true;
 
             // Collect stickies of users and check if broadcast is only registered for protected
             // broadcasts
@@ -12857,6 +12872,8 @@
                     // Change is not enabled, thus not targeting T+. Assume exported.
                     flags |= Context.RECEIVER_EXPORTED;
                 }
+            } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+                flags |= Context.RECEIVER_EXPORTED;
             }
         }
 
@@ -13349,6 +13366,13 @@
                     backgroundActivityStartsToken = null;
                 }
             }
+
+            // TODO (206518114): We need to use the "real" package name which sent the broadcast,
+            // in case the broadcast is sent via PendingIntent.
+            if (brOptions.getIdForResponseEvent() > 0) {
+                enforceUsageStatsPermission(callerPackage, realCallingUid, realCallingPid,
+                        "recordResponseEventWhileInBackground()");
+            }
         }
 
         // Verify that protected broadcasts are only being sent by system code,
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 1315293..465623f 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -54,6 +54,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
 import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
+import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP;
 import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
 import static android.os.PowerExemptionManager.REASON_DENIED;
 import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
@@ -65,6 +66,7 @@
 import static android.os.PowerExemptionManager.REASON_PROFILE_OWNER;
 import static android.os.PowerExemptionManager.REASON_ROLE_DIALER;
 import static android.os.PowerExemptionManager.REASON_ROLE_EMERGENCY;
+import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
 import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
 import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
 import static android.os.Process.SYSTEM_UID;
@@ -125,6 +127,7 @@
 import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -138,6 +141,7 @@
 import com.android.internal.util.function.TriConsumer;
 import com.android.server.AppStateTracker;
 import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.usage.AppStandbyInternal;
@@ -229,6 +233,24 @@
     @GuardedBy("mLock")
     private final HashMap<String, Boolean> mSystemModulesCache = new HashMap<>();
 
+    /**
+     * The pre-config packages that are exempted from the background restrictions.
+     */
+    private ArraySet<String> mBgRestrictionExemptioFromSysConfig;
+
+    /**
+     * Lock specifically for bookkeeping around the carrier-privileged app set.
+     * Do not acquire any other locks while holding this one. Methods that
+     * require this lock to be held are named with a "CPL" suffix.
+     */
+    private final Object mCarrierPrivilegedLock = new Object();
+
+    /**
+     * List of carrier-privileged apps that should be excluded from standby.
+     */
+    @GuardedBy("mCarrierPrivilegedLock")
+    private List<String> mCarrierPrivilegedApps;
+
     final ActivityManagerService mActivityManagerService;
 
     /**
@@ -690,6 +712,7 @@
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 ActivityThread.currentApplication().getMainExecutor(), mConstantsObserver);
         mConstantsObserver.start();
+        initBgRestrictionExemptioFromSysConfig();
         initRestrictionStates();
         initSystemModuleNames();
         registerForUidObservers();
@@ -711,6 +734,22 @@
         initRestrictionStates();
     }
 
+    private void initBgRestrictionExemptioFromSysConfig() {
+        mBgRestrictionExemptioFromSysConfig =
+                SystemConfig.getInstance().getBgRestrictionExemption();
+        if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+            final ArraySet<String> exemptedPkgs = mBgRestrictionExemptioFromSysConfig;
+            for (int i = exemptedPkgs.size() - 1; i >= 0; i--) {
+                Slog.i(TAG, "bg-restriction-exemption: " + exemptedPkgs.valueAt(i));
+            }
+        }
+    }
+
+    private boolean isExemptedFromSysConfig(String packageName) {
+        return mBgRestrictionExemptioFromSysConfig != null
+                && mBgRestrictionExemptioFromSysConfig.contains(packageName);
+    }
+
     private void initRestrictionStates() {
         final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
         for (int userId : allUsers) {
@@ -1542,14 +1581,11 @@
         }
     }
 
-    boolean isOnDeviceIdleAllowlist(int uid, boolean allowExceptIdle) {
+    boolean isOnDeviceIdleAllowlist(int uid) {
         final int appId = UserHandle.getAppId(uid);
 
-        final int[] allowlist = allowExceptIdle
-                ? mDeviceIdleExceptIdleAllowlist
-                : mDeviceIdleAllowlist;
-
-        return Arrays.binarySearch(allowlist, appId) >= 0;
+        return Arrays.binarySearch(mDeviceIdleAllowlist, appId) >= 0
+                || Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, appId) >= 0;
     }
 
     void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) {
@@ -1570,7 +1606,7 @@
         if (UserHandle.isCore(uid)) {
             return REASON_SYSTEM_UID;
         }
-        if (isOnDeviceIdleAllowlist(uid, false)) {
+        if (isOnDeviceIdleAllowlist(uid)) {
             return REASON_ALLOWLISTED_PACKAGE;
         }
         final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
@@ -1604,6 +1640,10 @@
                     return REASON_OP_ACTIVATE_PLATFORM_VPN;
                 } else if (isSystemModule(pkg)) {
                     return REASON_SYSTEM_MODULE;
+                } else if (isCarrierApp(pkg)) {
+                    return REASON_CARRIER_PRIVILEGED_APP;
+                } else if (isExemptedFromSysConfig(pkg)) {
+                    return REASON_SYSTEM_ALLOW_LISTED;
                 }
             }
         }
@@ -1616,6 +1656,37 @@
         return REASON_DENIED;
     }
 
+    private boolean isCarrierApp(String packageName) {
+        synchronized (mCarrierPrivilegedLock) {
+            if (mCarrierPrivilegedApps == null) {
+                fetchCarrierPrivilegedAppsCPL();
+            }
+            if (mCarrierPrivilegedApps != null) {
+                return mCarrierPrivilegedApps.contains(packageName);
+            }
+            return false;
+        }
+    }
+
+    private void clearCarrierPrivilegedApps() {
+        if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+            Slog.i(TAG, "Clearing carrier privileged apps list");
+        }
+        synchronized (mCarrierPrivilegedLock) {
+            mCarrierPrivilegedApps = null; // Need to be refetched.
+        }
+    }
+
+    @GuardedBy("mCarrierPrivilegedLock")
+    private void fetchCarrierPrivilegedAppsCPL() {
+        final TelephonyManager telephonyManager = mInjector.getTelephonyManager();
+        mCarrierPrivilegedApps =
+                telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
+        if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+            Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+        }
+    }
+
     private boolean isRoleHeldByUid(@NonNull String roleName, int uid) {
         synchronized (mLock) {
             final ArrayList<String> roles = mUidRolesMapping.get(uid);
@@ -1791,6 +1862,7 @@
         private AppBatteryExemptionTracker mAppBatteryExemptionTracker;
         private AppFGSTracker mAppFGSTracker;
         private AppMediaSessionTracker mAppMediaSessionTracker;
+        private TelephonyManager mTelephonyManager;
 
         Injector(Context context) {
             mContext = context;
@@ -1890,6 +1962,13 @@
             return mRoleManager;
         }
 
+        TelephonyManager getTelephonyManager() {
+            if (mTelephonyManager == null) {
+                mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
+            }
+            return mTelephonyManager;
+        }
+
         AppFGSTracker getAppFGSTracker() {
             return mAppFGSTracker;
         }
@@ -1939,6 +2018,19 @@
                                 onUidAdded(uid);
                             }
                         }
+                    }
+                    // fall through.
+                    case Intent.ACTION_PACKAGE_CHANGED: {
+                        final String pkgName = intent.getData().getSchemeSpecificPart();
+                        final String[] cmpList = intent.getStringArrayExtra(
+                                Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+                        // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package
+                        // enable/disable event (cmpList is just the package name itself), drop
+                        // our carrier privileged app & system-app caches and let them refresh
+                        if (cmpList == null
+                                || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) {
+                            clearCarrierPrivilegedApps();
+                        }
                     } break;
                     case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
                         final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -1986,6 +2078,7 @@
         };
         final IntentFilter packageFilter = new IntentFilter();
         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
         packageFilter.addDataScheme("package");
         mContext.registerReceiverForAllUsers(broadcastReceiver, packageFilter, null, mBgHandler);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e2921e9..a83fdd0 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -56,6 +56,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerExemptionManager;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.PowerExemptionManager.TempAllowListType;
 import android.os.Process;
@@ -870,7 +871,7 @@
                     + " due to receiver " + filter.receiverList.app
                     + " (uid " + filter.receiverList.uid + ")"
                     + " not specifying RECEIVER_EXPORTED");
-            // skip = true;
+            skip = true;
         }
 
         if (skip) {
@@ -1857,22 +1858,36 @@
     }
 
     private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
+        // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
+        // getIdForResponseEvent.
+        // TODO (217251579): Temporarily use temp-allowlist reason to identify
+        // push messages and record response events.
+        useTemporaryAllowlistReasonAsSignal(r);
+        if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
+            return;
+        }
         final String targetPackage = getTargetPackage(r);
         // Ignore non-explicit broadcasts
         if (targetPackage == null) {
             return;
         }
-        // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
-        // getIdForResponseEvent.
-        if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
-            return;
-        }
         getUsageStatsManagerInternal().reportBroadcastDispatched(
                 r.callingUid, targetPackage, UserHandle.of(r.userId),
                 r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
                 mService.getUidStateLocked(targetUid));
     }
 
+    private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) {
+        if (r.options == null || r.options.getIdForResponseEvent() > 0) {
+            return;
+        }
+        final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode();
+        if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING
+                || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) {
+            r.options.recordResponseEventWhileInBackground(reasonCode);
+        }
+    }
+
     @NonNull
     private UsageStatsManagerInternal getUsageStatsManagerInternal() {
         final UsageStatsManagerInternal usageStatsManagerInternal =
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3c9d29d..551773e 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -20,6 +20,11 @@
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 
+import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
+import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
+import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride;
+import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode;
+import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode;
 import static com.android.server.wm.CompatModePackages.DOWNSCALED;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
@@ -54,6 +59,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.hardware.power.Mode;
 import android.net.Uri;
 import android.os.Binder;
@@ -71,8 +80,10 @@
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
 import android.util.ArrayMap;
+import android.util.AttributeSet;
 import android.util.KeyValueListParser;
 import android.util.Slog;
+import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -84,7 +95,11 @@
 import com.android.server.SystemService;
 import com.android.server.SystemService.TargetUser;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.List;
 
@@ -425,12 +440,20 @@
         public static final String METADATA_BATTERY_MODE_ENABLE =
                 "com.android.app.gamemode.battery.enabled";
 
+        /**
+         * Metadata that allows a game to specify all intervention information with an XML file in
+         * the application field.
+         */
+        public static final String METADATA_GAME_MODE_CONFIG = "android.game_mode_config";
+
+        private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config";
         private final String mPackageName;
         private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
         private boolean mPerfModeOptedIn;
         private boolean mBatteryModeOptedIn;
         private boolean mAllowDownscale;
         private boolean mAllowAngle;
+        private boolean mAllowFpsOverride;
 
         GamePackageConfiguration(String packageName, int userId) {
             mPackageName = packageName;
@@ -438,18 +461,21 @@
             try {
                 final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
                         PackageManager.GET_META_DATA, userId);
-                if (ai.metaData != null) {
-                    mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
-                    mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
-                    mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
-                    mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
-                } else {
-                    mPerfModeOptedIn = false;
-                    mBatteryModeOptedIn = false;
-                    mAllowDownscale = true;
-                    mAllowAngle = true;
+                if (!parseInterventionFromXml(ai, packageName)) {
+                    if (ai.metaData != null) {
+                        mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
+                        mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
+                        mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
+                        mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
+                    } else {
+                        mPerfModeOptedIn = false;
+                        mBatteryModeOptedIn = false;
+                        mAllowDownscale = true;
+                        mAllowAngle = true;
+                        mAllowFpsOverride = true;
+                    }
                 }
-            } catch (PackageManager.NameNotFoundException e) {
+            } catch (NameNotFoundException e) {
                 // Not all packages are installed, hence ignore those that are not installed yet.
                 Slog.v(TAG, "Failed to get package metadata");
             }
@@ -469,6 +495,53 @@
             }
         }
 
+        private boolean parseInterventionFromXml(ApplicationInfo ai, String packageName) {
+            boolean xmlFound = false;
+            try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager,
+                    METADATA_GAME_MODE_CONFIG)) {
+                if (parser == null) {
+                    Slog.v(TAG, "No " + METADATA_GAME_MODE_CONFIG
+                            + " meta-data found for package " + mPackageName);
+                } else {
+                    xmlFound = true;
+                    final Resources resources = mPackageManager.getResourcesForApplication(
+                            packageName);
+                    final AttributeSet attributeSet = Xml.asAttributeSet(parser);
+                    int type;
+                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                            && type != XmlPullParser.START_TAG) {
+                        // Do nothing
+                    }
+
+                    boolean isStartingTagGameModeConfig =
+                            GAME_MODE_CONFIG_NODE_NAME.equals(parser.getName());
+                    if (!isStartingTagGameModeConfig) {
+                        Slog.w(TAG, "Meta-data does not start with "
+                                + GAME_MODE_CONFIG_NODE_NAME
+                                + " tag");
+                    } else {
+                        final TypedArray array = resources.obtainAttributes(attributeSet,
+                                com.android.internal.R.styleable.GameModeConfig);
+                        mPerfModeOptedIn = array.getBoolean(
+                                GameModeConfig_supportsPerformanceGameMode, false);
+                        mBatteryModeOptedIn = array.getBoolean(
+                                GameModeConfig_supportsBatteryGameMode,
+                                false);
+                        mAllowDownscale = array.getBoolean(GameModeConfig_allowGameDownscaling,
+                                true);
+                        mAllowAngle = array.getBoolean(GameModeConfig_allowGameAngleDriver, true);
+                        mAllowFpsOverride = array.getBoolean(GameModeConfig_allowGameFpsOverride,
+                                true);
+                        array.recycle();
+                    }
+                }
+            } catch (NameNotFoundException | XmlPullParserException | IOException ex) {
+                Slog.e(TAG, "Error while parsing XML meta-data for "
+                        + METADATA_GAME_MODE_CONFIG);
+            }
+            return xmlFound;
+        }
+
         /**
          * GameModeConfiguration contains all the values for all the interventions associated with
          * a game mode.
@@ -497,7 +570,8 @@
                 mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
                         ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
 
-                mFps = parser.getString(FPS_KEY, DEFAULT_FPS);
+                mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
+                        ? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
                 // We only want to use ANGLE if:
                 // - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
                 // - The app has not opted in to performing the work itself AND
@@ -691,7 +765,7 @@
         try {
             return mPackageManager.getPackageUidAsUser(packageName, userId)
                     == Binder.getCallingUid();
-        } catch (PackageManager.NameNotFoundException e) {
+        } catch (NameNotFoundException e) {
             return false;
         }
     }
@@ -855,7 +929,7 @@
      */
     @Override
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
-    public @GameMode boolean getAngleEnabled(String packageName, int userId)
+    public @GameMode boolean isAngleEnabled(String packageName, int userId)
             throws SecurityException {
         final int gameMode = getGameMode(packageName, userId);
         if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
@@ -1413,7 +1487,7 @@
                         if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
                             return;
                         }
-                    } catch (PackageManager.NameNotFoundException e) {
+                    } catch (NameNotFoundException e) {
                         // Ignore the exception.
                     }
                     switch (intent.getAction()) {
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index af1dd33..960fbf1 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -50,6 +50,7 @@
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.infra.ServiceConnector;
 import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
 import com.android.server.wm.WindowManagerService;
 
 import java.util.List;
@@ -62,6 +63,18 @@
     private static final int CREATE_GAME_SESSION_TIMEOUT_MS = 10_000;
     private static final boolean DEBUG = false;
 
+    private final TaskSystemBarsListener mTaskSystemBarsVisibilityListener =
+            new TaskSystemBarsListener() {
+                @Override
+                public void onTransientSystemBarsVisibilityChanged(
+                        int taskId,
+                        boolean visible,
+                        boolean wereRevealedFromSwipeOnSystemBar) {
+                    GameServiceProviderInstanceImpl.this.onTransientSystemBarsVisibilityChanged(
+                            taskId, visible, wereRevealedFromSwipeOnSystemBar);
+                }
+            };
+
     private final TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
@@ -203,6 +216,8 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to register task stack listener", e);
         }
+
+        mWindowManagerInternal.registerTaskSystemBarsListener(mTaskSystemBarsVisibilityListener);
     }
 
     @GuardedBy("mLock")
@@ -218,6 +233,9 @@
             Slog.w(TAG, "Failed to unregister task stack listener", e);
         }
 
+        mWindowManagerInternal.unregisterTaskSystemBarsListener(
+                mTaskSystemBarsVisibilityListener);
+
         for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
             destroyGameSessionFromRecord(gameSessionRecord);
         }
@@ -307,6 +325,37 @@
         }
     }
 
+    private void onTransientSystemBarsVisibilityChanged(
+            int taskId,
+            boolean visible,
+            boolean wereRevealedFromSwipeOnSystemBar) {
+        if (visible && !wereRevealedFromSwipeOnSystemBar) {
+            return;
+        }
+
+        GameSessionRecord gameSessionRecord;
+        synchronized (mLock) {
+            gameSessionRecord = mGameSessions.get(taskId);
+        }
+
+        if (gameSessionRecord == null) {
+            return;
+        }
+
+        IGameSession gameSession = gameSessionRecord.getGameSession();
+        if (gameSession == null) {
+            return;
+        }
+
+        try {
+            gameSession.onTransientSystemBarVisibilityFromRevealGestureChanged(visible);
+        } catch (RemoteException ex) {
+            Slog.w(TAG,
+                    "Failed to send transient system bars visibility from reveal gesture for task: "
+                            + taskId);
+        }
+    }
+
     private void createGameSession(int taskId) {
         synchronized (mLock) {
             createGameSessionLocked(taskId);
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index a139589..d2fa386 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -22,6 +22,7 @@
 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
 import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED;
 import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN;
+import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -29,6 +30,7 @@
 import android.app.ActivityThread;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -57,6 +59,7 @@
 import android.service.attention.AttentionService.AttentionSuccessCodes;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
+import android.service.attention.IProximityCallback;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -134,6 +137,15 @@
     @GuardedBy("mLock")
     AttentionCheck mCurrentAttentionCheck;
 
+    /**
+     * A proxy for relaying proximity information between the Attention Service and the client.
+     * The proxy will be initialized when the client calls onStartProximityUpdates and will be
+     * disabled only when the client calls onStopProximityUpdates.
+     */
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    ProximityUpdate mCurrentProximityUpdate;
+
     public AttentionManagerService(Context context) {
         this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE),
                 new Object(), null);
@@ -315,6 +327,77 @@
         }
     }
 
+    /**
+     * Requests the continuous updates of proximity signal via the provided callback,
+     * until the given callback is stopped.
+     *
+     * Calling this multiple times for duplicate requests will be no-ops, returning true.
+     *
+     * @return {@code true} if the framework was able to dispatch the request
+     */
+    @VisibleForTesting
+    boolean onStartProximityUpdates(ProximityCallbackInternal callbackInternal) {
+        Objects.requireNonNull(callbackInternal);
+        if (!mIsServiceEnabled) {
+            Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device.");
+            return false;
+        }
+
+        if (!isServiceAvailable()) {
+            Slog.w(LOG_TAG, "Service is not available at this moment.");
+            return false;
+        }
+
+        // don't allow proximity request in screen off state.
+        // This behavior might change in the future.
+        if (!mPowerManager.isInteractive()) {
+            Slog.w(LOG_TAG, "Proximity Service is unavailable during screen off at this moment.");
+            return false;
+        }
+
+        synchronized (mLock) {
+            // schedule shutting down the connection if no one resets this timer
+            freeIfInactiveLocked();
+
+            // lazily start the service, which should be very lightweight to start
+            bindLocked();
+
+            /*
+            Prevent spamming with multiple requests, only one at a time is allowed.
+            If there are use-cases for keeping track of multiple requests, we
+            can refactor ProximityUpdate object to keep track of multiple internal callbacks.
+             */
+            if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
+                if (mCurrentProximityUpdate.mCallbackInternal == callbackInternal) {
+                    Slog.w(LOG_TAG, "Provided callback is already registered. Skipping.");
+                    return true;
+                } else {
+                    // reject the new request since the old request is still alive.
+                    Slog.w(LOG_TAG, "New proximity update cannot be processed because there is "
+                            + "already an ongoing update");
+                    return false;
+                }
+            }
+            mCurrentProximityUpdate = new ProximityUpdate(callbackInternal);
+            return mCurrentProximityUpdate.startUpdates();
+        }
+    }
+
+    /** Cancels the specified proximity registration. */
+    @VisibleForTesting
+    void onStopProximityUpdates(ProximityCallbackInternal callbackInternal) {
+        synchronized (mLock) {
+            if (mCurrentProximityUpdate == null
+                    || !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal)
+                    || !mCurrentProximityUpdate.mStartedUpdates) {
+                Slog.w(LOG_TAG, "Cannot stop a non-current callback");
+                return;
+            }
+            mCurrentProximityUpdate.cancelUpdates();
+            mCurrentProximityUpdate = null;
+        }
+    }
+
     @GuardedBy("mLock")
     @VisibleForTesting
     protected void freeIfInactiveLocked() {
@@ -390,15 +473,18 @@
             ipw.println("Class=" + mComponentName.getClassName());
             ipw.decreaseIndent();
         }
-        ipw.println("binding=" + mBinding);
-        ipw.println("current attention check:");
         synchronized (mLock) {
+            ipw.println("binding=" + mBinding);
+            ipw.println("current attention check:");
             if (mCurrentAttentionCheck != null) {
                 mCurrentAttentionCheck.dump(ipw);
             }
             if (mAttentionCheckCacheBuffer != null) {
                 mAttentionCheckCacheBuffer.dump(ipw);
             }
+            if (mCurrentProximityUpdate != null) {
+                mCurrentProximityUpdate.dump(ipw);
+            }
         }
     }
 
@@ -417,6 +503,17 @@
         public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
             AttentionManagerService.this.cancelAttentionCheck(callbackInternal);
         }
+
+        @Override
+        public boolean onStartProximityUpdates(
+                ProximityCallbackInternal callback) {
+            return AttentionManagerService.this.onStartProximityUpdates(callback);
+        }
+
+        @Override
+        public void onStopProximityUpdates(ProximityCallbackInternal callback) {
+            AttentionManagerService.this.onStopProximityUpdates(callback);
+        }
     }
 
     @VisibleForTesting
@@ -536,6 +633,71 @@
         }
     }
 
+    @VisibleForTesting
+    final class ProximityUpdate {
+        private final ProximityCallbackInternal mCallbackInternal;
+        private final IProximityCallback mIProximityCallback;
+        private boolean mStartedUpdates;
+
+        ProximityUpdate(ProximityCallbackInternal callbackInternal) {
+            mCallbackInternal = callbackInternal;
+            mIProximityCallback = new IProximityCallback.Stub() {
+                @Override
+                public void onProximityUpdate(double distance) {
+                    synchronized (mLock) {
+                        mCallbackInternal.onProximityUpdate(distance);
+                        freeIfInactiveLocked();
+                    }
+                }
+            };
+        }
+
+        boolean startUpdates() {
+            synchronized (mLock) {
+                if (mStartedUpdates) {
+                    Slog.w(LOG_TAG, "Already registered to a proximity service.");
+                    return false;
+                }
+                if (mService == null) {
+                    Slog.w(LOG_TAG,
+                            "There is no service bound. Proximity update request rejected.");
+                    return false;
+                }
+                try {
+                    mService.onStartProximityUpdates(mIProximityCallback);
+                    mStartedUpdates = true;
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        void cancelUpdates() {
+            synchronized (mLock) {
+                if (mStartedUpdates) {
+                    if (mService == null) {
+                        mStartedUpdates = false;
+                        return;
+                    }
+                    try {
+                        mService.onStopProximityUpdates();
+                        mStartedUpdates = false;
+                    } catch (RemoteException e) {
+                        Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
+                    }
+                }
+            }
+        }
+
+        void dump(IndentingPrintWriter ipw) {
+            ipw.increaseIndent();
+            ipw.println("is StartedUpdates=" + mStartedUpdates);
+            ipw.decreaseIndent();
+        }
+    }
+
     private void appendResultToAttentionCacheBuffer(AttentionCheckCache cache) {
         synchronized (mLock) {
             if (mAttentionCheckCacheBuffer == null) {
@@ -593,6 +755,18 @@
                 mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN);
             }
         }
+        if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
+            if (mService != null) {
+                try {
+                    mService.onStartProximityUpdates(mCurrentProximityUpdate.mIProximityCallback);
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
+                }
+            } else {
+                mCurrentProximityUpdate.cancelUpdates();
+                mCurrentProximityUpdate = null;
+            }
+        }
     }
 
     @VisibleForTesting
@@ -609,7 +783,9 @@
             switch (msg.what) {
                 // Do not occupy resources when not in use - unbind proactively.
                 case CHECK_CONNECTION_EXPIRATION: {
-                    cancelAndUnbindLocked();
+                    synchronized (mLock) {
+                        cancelAndUnbindLocked();
+                    }
                 }
                 break;
 
@@ -653,10 +829,15 @@
     @GuardedBy("mLock")
     private void cancelAndUnbindLocked() {
         synchronized (mLock) {
-            if (mCurrentAttentionCheck == null) {
+            if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) {
                 return;
             }
-            cancel();
+            if (mCurrentAttentionCheck != null) {
+                cancel();
+            }
+            if (mCurrentProximityUpdate != null) {
+                mCurrentProximityUpdate.cancelUpdates();
+            }
             if (mService == null) {
                 return;
             }
@@ -702,7 +883,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
-                cancelAndUnbindLocked();
+                synchronized (mLock) {
+                    cancelAndUnbindLocked();
+                }
             }
         }
     }
@@ -730,8 +913,27 @@
             }
         }
 
+        class TestableProximityCallbackInternal extends ProximityCallbackInternal {
+            private double mLastCallbackCode = PROXIMITY_UNKNOWN;
+
+            @Override
+            public void onProximityUpdate(double distance) {
+                mLastCallbackCode = distance;
+            }
+
+            public void reset() {
+                mLastCallbackCode = PROXIMITY_UNKNOWN;
+            }
+
+            public double getLastCallbackCode() {
+                return mLastCallbackCode;
+            }
+        }
+
         final TestableAttentionCallbackInternal mTestableAttentionCallback =
                 new TestableAttentionCallbackInternal();
+        final TestableProximityCallbackInternal mTestableProximityCallback =
+                new TestableProximityCallbackInternal();
 
         @Override
         public int onCommand(@Nullable final String cmd) {
@@ -749,6 +951,10 @@
                                 return cmdCallCheckAttention();
                             case "cancelCheckAttention":
                                 return cmdCallCancelAttention();
+                            case "onStartProximityUpdates":
+                                return cmdCallOnStartProximityUpdates();
+                            case "onStopProximityUpdates":
+                                return cmdCallOnStopProximityUpdates();
                             default:
                                 throw new IllegalArgumentException("Invalid argument");
                         }
@@ -758,6 +964,8 @@
                         return cmdClearTestableAttentionService();
                     case "getLastTestCallbackCode":
                         return cmdGetLastTestCallbackCode();
+                    case "getLastTestProximityCallbackCode":
+                        return cmdGetLastTestProximityCallbackCode();
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -782,6 +990,7 @@
         private int cmdClearTestableAttentionService() {
             sTestAttentionServicePackage = "";
             mTestableAttentionCallback.reset();
+            mTestableProximityCallback.reset();
             resetStates();
             return 0;
         }
@@ -800,6 +1009,20 @@
             return 0;
         }
 
+        private int cmdCallOnStartProximityUpdates() {
+            final PrintWriter out = getOutPrintWriter();
+            boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityCallback);
+            out.println(calledSuccessfully ? "true" : "false");
+            return 0;
+        }
+
+        private int cmdCallOnStopProximityUpdates() {
+            final PrintWriter out = getOutPrintWriter();
+            onStopProximityUpdates(mTestableProximityCallback);
+            out.println("true");
+            return 0;
+        }
+
         private int cmdResolveAttentionServiceComponent() {
             final PrintWriter out = getOutPrintWriter();
             ComponentName resolvedComponent = resolveAttentionService(mContext);
@@ -813,7 +1036,16 @@
             return 0;
         }
 
+        private int cmdGetLastTestProximityCallbackCode() {
+            final PrintWriter out = getOutPrintWriter();
+            out.println(mTestableProximityCallback.getLastCallbackCode());
+            return 0;
+        }
+
         private void resetStates() {
+            synchronized (mLock) {
+                mCurrentProximityUpdate = null;
+            }
             mComponentName = resolveAttentionService(mContext);
         }
 
@@ -844,11 +1076,24 @@
                             + " (to see the result, call getLastTestCallbackCode)");
             out.println("       := false, otherwise");
             out.println("  call cancelCheckAttention: Cancels check attention");
+            out.println("  call onStartProximityUpdates: Calls onStartProximityUpdates");
+            out.println("  ---returns:");
+            out.println(
+                    "       := true, if the request was successfully dispatched to the service "
+                            + "implementation."
+                            + " (to see the result, call getLastTestProximityCallbackCode)");
+            out.println("       := false, otherwise");
+            out.println("  call onStopProximityUpdates: Cancels proximity updates");
             out.println("  getLastTestCallbackCode");
             out.println("  ---returns:");
             out.println(
                     "       := An integer, representing the last callback code received from the "
                             + "bounded implementation. If none, it will return -1");
+            out.println("  getLastTestProximityCallbackCode");
+            out.println("  ---returns:");
+            out.println(
+                    "       := A double, representing the last proximity value received from the "
+                            + "bounded implementation. If none, it will return -1.0");
         }
     }
 
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 28508f4..53fe450 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -1090,6 +1090,10 @@
                 && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
             return;
         }
+        if (mPm.checkPermission(Manifest.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION,
+                callingPackage) == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
         // Don't notify if already notified for this uid and clip.
         if (clipboard.mNotifiedUids.get(uid)) {
             return;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1b55257..162eb0e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -201,6 +201,10 @@
     // Controls High Brightness Mode.
     private HighBrightnessModeController mHbmController;
 
+    // Throttles (caps) maximum allowed brightness
+    private BrightnessThrottler mBrightnessThrottler;
+    private boolean mIsBrightnessThrottled;
+
     // Context-sensitive brightness configurations require keeping track of the foreground app's
     // package name and category, which is done by registering a TaskStackListener to call back to
     // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
@@ -226,7 +230,7 @@
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, Context context,
-            HighBrightnessModeController hbmController,
+            HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
             BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
             int ambientLightHorizonLong) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor,
@@ -235,8 +239,8 @@
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
                 ambientBrightnessThresholds, screenBrightnessThresholds, context,
-                hbmController, idleModeBrightnessMapper, ambientLightHorizonShort,
-                ambientLightHorizonLong
+                hbmController, brightnessThrottler, idleModeBrightnessMapper,
+                ambientLightHorizonShort, ambientLightHorizonLong
         );
     }
 
@@ -249,7 +253,7 @@
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, Context context,
-            HighBrightnessModeController hbmController,
+            HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
             BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
             int ambientLightHorizonLong) {
         mInjector = injector;
@@ -291,6 +295,7 @@
         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mHbmController = hbmController;
+        mBrightnessThrottler = brightnessThrottler;
         mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper;
         mIdleModeBrightnessMapper = idleModeBrightnessMapper;
         // Initialize to active (normal) screen brightness mode
@@ -365,6 +370,13 @@
             prepareBrightnessAdjustmentSample();
         }
         changed |= setLightSensorEnabled(enable && !dozing);
+
+        if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
+            // Maximum brightness has changed, so recalculate display brightness.
+            mIsBrightnessThrottled = mBrightnessThrottler.isThrottled();
+            changed = true;
+        }
+
         if (changed) {
             updateAutoBrightness(false /*sendUpdate*/, userInitiatedChange);
         }
@@ -855,8 +867,11 @@
 
     // Clamps values with float range [0.0-1.0]
     private float clampScreenBrightness(float value) {
-        return MathUtils.constrain(value,
-                mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
+        final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
+                mBrightnessThrottler.getBrightnessCap());
+        final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
+                mBrightnessThrottler.getBrightnessCap());
+        return MathUtils.constrain(value, minBrightness, maxBrightness);
     }
 
     private void prepareBrightnessAdjustmentSample() {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index d0ce9ef..5de162c 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -222,6 +222,22 @@
     }
 
     /**
+     * Returns the system preferred display mode.
+     */
+    public Display.Mode getSystemPreferredDisplayModeLocked() {
+        return EMPTY_DISPLAY_MODE;
+    }
+
+    /**
+     * Returns the display mode that was being used when this display was first found by
+     * display manager.
+     * @hide
+     */
+    public Display.Mode getActiveDisplayModeAtStartLocked() {
+        return EMPTY_DISPLAY_MODE;
+    }
+
+    /**
      * Sets the requested color mode.
      */
     public void setRequestedColorModeLocked(int colorMode) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4e88acd..7f1482e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1833,6 +1833,16 @@
         }
     }
 
+    Display.Mode getSystemPreferredDisplayModeInternal(int displayId) {
+        synchronized (mSyncRoot) {
+            final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+            if (device == null) {
+                return null;
+            }
+            return device.getSystemPreferredDisplayModeLocked();
+        }
+    }
+
     void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
         mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
     }
@@ -2183,6 +2193,16 @@
         }
     }
 
+    Display.Mode getActiveDisplayModeAtStart(int displayId) {
+        synchronized (mSyncRoot) {
+            final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+            if (device == null) {
+                return null;
+            }
+            return device.getActiveDisplayModeAtStartLocked();
+        }
+    }
+
     void setAmbientColorTemperatureOverride(float cct) {
         synchronized (mSyncRoot) {
             final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
@@ -3471,6 +3491,16 @@
         }
 
         @Override // Binder call
+        public Display.Mode getSystemPreferredDisplayMode(int displayId) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getSystemPreferredDisplayModeInternal(displayId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index a9875c8..bfdac57 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -68,6 +68,8 @@
                 return clearUserPreferredDisplayMode();
             case "get-user-preferred-display-mode":
                 return getUserPreferredDisplayMode();
+            case "get-active-display-mode-at-start":
+                return getActiveDisplayModeAtStart();
             case "set-match-content-frame-rate-pref":
                 return setMatchContentFrameRateUserPreference();
             case "get-match-content-frame-rate-pref":
@@ -125,6 +127,9 @@
         pw.println("    Returns the user preferred display mode or null if no mode is set by user."
                 + "If DISPLAY_ID is passed, the mode for display with id = DISPLAY_ID is "
                 + "returned, else global display mode is returned.");
+        pw.println("  get-active-display-mode-at-start DISPLAY_ID");
+        pw.println("    Returns the display mode which was found at boot time of display with "
+                + "id = DISPLAY_ID");
         pw.println("  set-match-content-frame-rate-pref PREFERENCE");
         pw.println("    Sets the match content frame rate preference as PREFERENCE ");
         pw.println("  get-match-content-frame-rate-pref");
@@ -298,6 +303,30 @@
         return 0;
     }
 
+    private int getActiveDisplayModeAtStart() {
+        final String displayIdText = getNextArg();
+        if (displayIdText == null) {
+            getErrPrintWriter().println("Error: no displayId specified");
+            return 1;
+        }
+        final int displayId;
+        try {
+            displayId = Integer.parseInt(displayIdText);
+        } catch (NumberFormatException e) {
+            getErrPrintWriter().println("Error: invalid displayId");
+            return 1;
+        }
+
+        Display.Mode mode = mService.getActiveDisplayModeAtStart(displayId);
+        if (mode == null) {
+            getOutPrintWriter().println("Boot display mode: null");
+            return 0;
+        }
+        getOutPrintWriter().println("Boot display mode: " + mode.getPhysicalWidth() + " "
+                + mode.getPhysicalHeight() + " " + mode.getRefreshRate());
+        return 0;
+    }
+
     private int setMatchContentFrameRateUserPreference() {
         final String matchContentFrameRatePrefText = getNextArg();
         if (matchContentFrameRatePrefText == null) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 3494342..6ae1a5a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -974,7 +974,7 @@
                     lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
                     darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
                     ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
-                    mHbmController, mIdleModeBrightnessMapper,
+                    mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
                     mDisplayDeviceConfig.getAmbientHorizonShort(),
                     mDisplayDeviceConfig.getAmbientHorizonLong());
         } else {
@@ -1347,6 +1347,29 @@
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
         }
 
+        // Now that a desired brightness has been calculated, apply brightness throttling. The
+        // dimming and low power transformations that follow can only dim brightness further.
+        //
+        // We didn't do this earlier through brightness clamping because we need to know both
+        // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations.
+        // Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
+        // we broadcast this change through setting.
+        final float unthrottledBrightnessState = brightnessState;
+        if (mBrightnessThrottler.isThrottled()) {
+            brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
+            mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
+            if (!mAppliedThrottling) {
+                // Brightness throttling is needed, so do so quickly.
+                // Later, when throttling is removed, we let other mechanisms decide on speed.
+                slowChange = false;
+                updateScreenBrightnessSetting = true;
+            }
+            mAppliedThrottling = true;
+        } else if (mAppliedThrottling) {
+            mAppliedThrottling = false;
+            updateScreenBrightnessSetting = true;
+        }
+
         if (updateScreenBrightnessSetting) {
             // Tell the rest of the system about the new brightness in case we had to change it
             // for things like auto-brightness or high-brightness-mode. Note that we do this
@@ -1393,20 +1416,6 @@
             mAppliedLowPower = false;
         }
 
-        // Apply brightness throttling after applying all other transforms
-        final float unthrottledBrightnessState = brightnessState;
-        if (mBrightnessThrottler.isThrottled()) {
-            brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
-            mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
-            if (!mAppliedThrottling) {
-                slowChange = false;
-            }
-            mAppliedThrottling = true;
-        } else if (mAppliedThrottling) {
-            slowChange = false;
-            mAppliedThrottling = false;
-        }
-
         // The current brightness to use has been calculated at this point, and HbmController should
         // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
         // here instead of having HbmController listen to the brightness setting because certain
@@ -1656,6 +1665,10 @@
 
     private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
         synchronized (mCachedBrightnessInfo) {
+            final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
+                    mBrightnessThrottler.getBrightnessCap());
+            final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
+                    mBrightnessThrottler.getBrightnessCap());
             boolean changed = false;
 
             changed |=
@@ -1666,10 +1679,10 @@
                         adjustedBrightness);
             changed |=
                 mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
-                        mHbmController.getCurrentBrightnessMin());
+                        minBrightness);
             changed |=
                 mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
-                        mHbmController.getCurrentBrightnessMax());
+                        maxBrightness);
             changed |=
                 mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
                         mHbmController.getHighBrightnessMode());
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3a9ef0a..a31c231 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -192,8 +192,12 @@
         private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         private int mDefaultModeId = INVALID_MODE_ID;
+        private int mSystemPreferredModeId = INVALID_MODE_ID;
         private int mDefaultModeGroup;
         private int mUserPreferredModeId = INVALID_MODE_ID;
+        // This is used only for the purpose of testing, to verify if the mode was correct when the
+        // device started or booted.
+        private int mActiveDisplayModeAtStartId = INVALID_MODE_ID;
         private Display.Mode mUserPreferredMode;
         private int mActiveModeId = INVALID_MODE_ID;
         private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
@@ -208,7 +212,7 @@
         private boolean mSidekickActive;
         private SidekickInternal mSidekickInternal;
         private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
-        // The supported display modes according in SurfaceFlinger
+        // The supported display modes according to SurfaceFlinger
         private SurfaceControl.DisplayMode[] mSfDisplayModes;
         // The active display mode in SurfaceFlinger
         private SurfaceControl.DisplayMode mActiveSfDisplayMode;
@@ -230,6 +234,7 @@
             mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay,
                     mSurfaceControlProxy);
             mDisplayDeviceConfig = null;
+            mActiveDisplayModeAtStartId = dynamicInfo.activeDisplayModeId;
         }
 
         @Override
@@ -238,12 +243,23 @@
         }
 
         /**
+         * Returns the boot display mode of this display.
+         * @hide
+         */
+        @Override
+        public Display.Mode getActiveDisplayModeAtStartLocked() {
+            return findMode(mActiveDisplayModeAtStartId);
+        }
+
+        /**
          * Returns true if there is a change.
          **/
         public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
                 SurfaceControl.DynamicDisplayInfo dynamicInfo,
                 SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
-            boolean changed = updateDisplayModesLocked(
+            boolean changed =
+                    updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode);
+            changed |= updateDisplayModesLocked(
                     dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
             changed |= updateStaticInfo(staticInfo);
             changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
@@ -369,8 +385,11 @@
 
             // For a new display, we need to initialize the default mode ID.
             if (mDefaultModeId == INVALID_MODE_ID) {
-                mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultModeGroup = mActiveSfDisplayMode.group;
+                mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
+                        ? mSystemPreferredModeId : activeRecord.mMode.getModeId();
+                mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
+                        ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group
+                        : mActiveSfDisplayMode.group;
             } else if (modesAdded && activeModeChanged) {
                 Slog.d(TAG, "New display modes are added and the active mode has changed, "
                         + "use active mode as default mode.");
@@ -531,6 +550,15 @@
             return true;
         }
 
+        private boolean updateSystemPreferredDisplayMode(int modeId) {
+            if (!mSurfaceControlProxy.getBootDisplayModeSupport()
+                    || mSystemPreferredModeId == modeId) {
+                return false;
+            }
+            mSystemPreferredModeId = modeId;
+            return true;
+        }
+
         private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
                 int modeId) {
             for (SurfaceControl.DisplayMode mode : supportedModes) {
@@ -857,6 +885,16 @@
             if (oldModeId != getPreferredModeId()) {
                 updateDeviceInfoLocked();
             }
+
+            if (!mSurfaceControlProxy.getBootDisplayModeSupport()) {
+                return;
+            }
+            if (mUserPreferredMode == null) {
+                mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
+            } else {
+                mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
+                        mUserPreferredMode.getModeId());
+            }
         }
 
         @Override
@@ -865,6 +903,11 @@
         }
 
         @Override
+        public Display.Mode getSystemPreferredDisplayModeLocked() {
+            return findMode(mSystemPreferredModeId);
+        }
+
+        @Override
         public void setRequestedColorModeLocked(int colorMode) {
             requestColorModeLocked(colorMode);
         }
@@ -1072,6 +1115,17 @@
             return matchingModeId;
         }
 
+        // Returns a mode with id = modeId.
+        private Display.Mode findMode(int modeId) {
+            for (int i = 0; i < mSupportedModes.size(); i++) {
+                Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode;
+                if (supportedMode.getModeId() == modeId) {
+                    return supportedMode;
+                }
+            }
+            return null;
+        }
+
        // Returns a mode with resolution (width, height) and/or refreshRate. If any one of the
        // resolution or refresh-rate is valid, a mode having the valid parameters is returned.
         private Display.Mode findMode(int width, int height, float refreshRate) {
@@ -1318,6 +1372,18 @@
             return SurfaceControl.setActiveColorMode(displayToken, colorMode);
         }
 
+        public boolean getBootDisplayModeSupport() {
+            return SurfaceControl.getBootDisplayModeSupport();
+        }
+
+        public void setBootDisplayMode(IBinder displayToken, int modeId) {
+            SurfaceControl.setBootDisplayMode(displayToken, modeId);
+        }
+
+        public void clearBootDisplayMode(IBinder displayToken) {
+            SurfaceControl.clearBootDisplayMode(displayToken);
+        }
+
         public void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
             SurfaceControl.setAutoLowLatencyMode(displayToken, on);
 
@@ -1340,7 +1406,6 @@
             return SurfaceControl.setDisplayBrightness(displayToken, sdrBacklight, sdrNits,
                     displayBacklight, displayNits);
         }
-
     }
 
     static class BacklightAdapter {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 1ebd1f5a..d8672fc 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -70,7 +70,7 @@
                 mRate = 0;
                 mTargetValue = target;
                 mCurrentValue = target;
-                mProperty.setValue(mObject, target);
+                setPropertyValue(target);
                 if (mAnimating) {
                     mAnimating = false;
                     cancelAnimationCallback();
@@ -125,6 +125,15 @@
         mListener = listener;
     }
 
+    /**
+     * Sets the brightness property by converting the given value from HLG space
+     * into linear space.
+     */
+    private void setPropertyValue(float val) {
+        final float linearVal = BrightnessUtils.convertGammaToLinear(val);
+        mProperty.setValue(mObject, linearVal);
+    }
+
     private void postAnimationCallback() {
         mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
     }
@@ -160,9 +169,7 @@
             final float oldCurrentValue = mCurrentValue;
             mCurrentValue = mAnimatedValue;
             if (oldCurrentValue != mCurrentValue) {
-                // Convert value from HLG into linear space for the property.
-                final float linearCurrentVal = BrightnessUtils.convertGammaToLinear(mCurrentValue);
-                mProperty.setValue(mObject, linearCurrentVal);
+                setPropertyValue(mCurrentValue);
             }
             if (mTargetValue != mCurrentValue) {
                 postAnimationCallback();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 65ec1c0..79820a2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -29,6 +29,7 @@
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
 import android.util.ArrayMap;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -246,9 +247,11 @@
                 mAllowedValues.add(value);
                 if (mContext.getResources().getBoolean(defaultResId)) {
                     if (mDefaultValue != null) {
-                        throw new VerificationException("Invalid CEC setup for '"
-                            + this.getName() + "' setting. "
-                            + "Setting already has a default value.");
+                        Slog.e(TAG,
+                                "Failed to set '" + value + "' as a default for '" + this.getName()
+                                        + "': Setting already has a default ('" + mDefaultValue
+                                        + "').");
+                        return;
                     }
                     mDefaultValue = value;
                 }
@@ -277,6 +280,11 @@
         mContext = context;
         mStorageAdapter = storageAdapter;
 
+        // IMPORTANT: when adding a config value for a particular setting, register that value AFTER
+        // the existing values for that setting. That way, defaults set in the RRO are forward
+        // compatible even if the RRO doesn't include that new value yet
+        // (e.g. because it's ported from a previous release).
+
         Setting hdmiCecEnabled = registerSetting(
                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                 R.bool.config_cecHdmiCecEnabled_userConfigurable);
@@ -313,15 +321,15 @@
         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
                 R.bool.config_cecPowerControlModeTv_allowed,
                 R.bool.config_cecPowerControlModeTv_default);
-        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
-                R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
-                R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
                 R.bool.config_cecPowerControlModeBroadcast_allowed,
                 R.bool.config_cecPowerControlModeBroadcast_default);
         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE,
                 R.bool.config_cecPowerControlModeNone_allowed,
                 R.bool.config_cecPowerControlModeNone_default);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
+                R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
+                R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
 
         Setting powerStateChangeOnActiveSourceLost = registerSetting(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
@@ -385,6 +393,16 @@
                 R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed,
                 R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
 
+        Setting setMenuLanguage = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
+                R.bool.config_cecSetMenuLanguage_userConfigurable);
+        setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_ENABLED,
+                R.bool.config_cecSetMenuLanguageEnabled_allowed,
+                R.bool.config_cecSetMenuLanguageEnabled_default);
+        setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_DISABLED,
+                R.bool.config_cecSetMenuLanguageDisabled_allowed,
+                R.bool.config_cecSetMenuLanguageDisabled_default);
+
         Setting rcProfileTv = registerSetting(
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
                 R.bool.config_cecRcProfileTv_userConfigurable);
@@ -697,6 +715,8 @@
                 return STORAGE_SHARED_PREFS;
             case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
                 return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE:
+                return STORAGE_SHARED_PREFS;
             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
                 return STORAGE_SHARED_PREFS;
             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
@@ -768,6 +788,8 @@
                 return setting.getName();
             case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
                 return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE:
+                return setting.getName();
             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
                 return setting.getName();
             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c674ffe..454a76a 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -901,17 +901,12 @@
     protected int handleVendorCommandWithId(HdmiCecMessage message) {
         byte[] params = message.getParams();
         int vendorId = HdmiUtils.threeBytesToInt(params);
-        if (vendorId == mService.getVendorId()) {
-            if (!mService.invokeVendorCommandListenersOnReceived(
-                    mDeviceType, message.getSource(), message.getDestination(), params, true)) {
-                return Constants.ABORT_REFUSED;
-            }
-        } else if (message.getDestination() != Constants.ADDR_BROADCAST
-                && message.getSource() != Constants.ADDR_UNREGISTERED) {
-            Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
-            return Constants.ABORT_UNRECOGNIZED_OPCODE;
-        } else {
+        if (message.getDestination() == Constants.ADDR_BROADCAST
+                || message.getSource() == Constants.ADDR_UNREGISTERED) {
             Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+        } else if (!mService.invokeVendorCommandListenersOnReceived(
+                mDeviceType, message.getSource(), message.getDestination(), params, true)) {
+            return Constants.ABORT_REFUSED;
         }
         return Constants.HANDLED;
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index f413fbd..0edcea5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -50,9 +50,6 @@
 public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
     private static final String TAG = "HdmiCecLocalDevicePlayback";
 
-    private static final boolean SET_MENU_LANGUAGE =
-            HdmiProperties.set_menu_language_enabled().orElse(false);
-
     // How long to wait after hotplug out before possibly going to Standby.
     @VisibleForTesting
     static final long STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS = 30_000;
@@ -388,7 +385,9 @@
     @Constants.HandleMessageResult
     protected int handleSetMenuLanguage(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (!SET_MENU_LANGUAGE) {
+        if (mService.getHdmiCecConfig().getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE)
+                    == HdmiControlManager.SET_MENU_LANGUAGE_DISABLED) {
             return Constants.ABORT_UNRECOGNIZED_OPCODE;
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 6f7473d..57fe9e6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -133,6 +133,7 @@
         addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
         addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
         addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
+        addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
 
         addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8391e0b..8ac2331 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1701,11 +1701,11 @@
 
     class VendorCommandListenerRecord implements IBinder.DeathRecipient {
         private final IHdmiVendorCommandListener mListener;
-        private final int mDeviceType;
+        private final int mVendorId;
 
-        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+        VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) {
             mListener = listener;
-            mDeviceType = deviceType;
+            mVendorId = vendorId;
         }
 
         @Override
@@ -2191,10 +2191,10 @@
         }
 
         @Override
-        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
-                final int deviceType) {
+        public void addVendorCommandListener(
+                final IHdmiVendorCommandListener listener, final int vendorId) {
             initBinderCall();
-            HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+            HdmiControlService.this.addVendorCommandListener(listener, vendorId);
         }
 
         @Override
@@ -3354,8 +3354,9 @@
         mStandbyMessageReceived = false;
     }
 
-    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
-        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+    @VisibleForTesting
+    void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
         try {
             listener.asBinder().linkToDeath(record, 0);
         } catch (RemoteException e) {
@@ -3374,8 +3375,14 @@
                 return false;
             }
             for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
-                if (record.mDeviceType != deviceType) {
-                    continue;
+                if (hasVendorId) {
+                    int vendorId =
+                            ((params[0] & 0xFF) << 16)
+                                    + ((params[1] & 0xFF) << 8)
+                                    + (params[2] & 0xFF);
+                    if (record.mVendorId != vendorId) {
+                        continue;
+                    }
                 }
                 try {
                     record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
new file mode 100644
index 0000000..dbd3f35
--- /dev/null
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+
+import android.os.IBinder;
+import android.view.InputApplicationHandle;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+/**
+ * An internal implementation of an {@link InputMonitor} that uses a spy window.
+ *
+ * This spy window is a layer container in the SurfaceFlinger hierarchy that does not have any
+ * graphical buffer, but can still receive input. It is parented to the DisplayContent so
+ * that it can spy on any pointer events that start in the DisplayContent bounds. When the
+ * object is constructed, it will add itself to SurfaceFlinger.
+ */
+class GestureMonitorSpyWindow {
+    final InputApplicationHandle mApplicationHandle;
+    final InputWindowHandle mWindowHandle;
+
+    // The token, InputChannel, and SurfaceControl are owned by this object.
+    final IBinder mMonitorToken;
+    final InputChannel mClientChannel;
+    final SurfaceControl mInputSurface;
+
+    GestureMonitorSpyWindow(IBinder token, String name, int displayId, int pid, int uid,
+            SurfaceControl sc, InputChannel inputChannel) {
+        mMonitorToken = token;
+        mClientChannel = inputChannel;
+        mInputSurface = sc;
+
+        mApplicationHandle = new InputApplicationHandle(null, name,
+                DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
+        mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
+
+        mWindowHandle.name = name;
+        mWindowHandle.token = mClientChannel.getToken();
+        mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+        mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+        mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+        mWindowHandle.visible = true;
+        mWindowHandle.focusable = false;
+        mWindowHandle.hasWallpaper = false;
+        mWindowHandle.paused = false;
+        mWindowHandle.ownerPid = pid;
+        mWindowHandle.ownerUid = uid;
+        mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+        mWindowHandle.scaleFactor = 1.0f;
+        mWindowHandle.trustedOverlay = true;
+        mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
+
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.setInputWindowInfo(mInputSurface, mWindowHandle);
+        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+        t.setPosition(mInputSurface, 0, 0);
+        t.setCrop(mInputSurface, null /* crop to parent surface */);
+        t.show(mInputSurface);
+
+        t.apply();
+    }
+
+    void remove() {
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.hide(mInputSurface);
+        t.remove(mInputSurface);
+        t.apply();
+
+        mClientChannel.dispose();
+    }
+
+    String dump() {
+        return "name='" + mWindowHandle.name + "', inputChannelToken="
+                + mClientChannel.getToken() + " displayId=" + mWindowHandle.displayId;
+    }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 783a88c..bfaa7b3 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -150,6 +150,8 @@
     static final String TAG = "InputManager";
     static final boolean DEBUG = false;
 
+    private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = true;
+
     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
     private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
 
@@ -277,6 +279,12 @@
     @GuardedBy("mPointerDisplayIdLock")
     private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
 
+
+    // Holds all the registered gesture monitors that are implemented as spy windows. The spy
+    // windows are mapped by their InputChannel tokens.
+    @GuardedBy("mInputMonitors")
+    final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>();
+
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
     private static native void nativeStart(long ptr);
@@ -716,33 +724,77 @@
                 inputChannelName, Binder.getCallingPid());
     }
 
+    @NonNull
+    private InputChannel createSpyWindowGestureMonitor(IBinder monitorToken, String name,
+            int displayId, int pid, int uid) {
+        final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
+                displayId);
+        if (sc == null) {
+            throw new IllegalArgumentException(
+                    "Could not create gesture monitor surface on display: " + displayId);
+        }
+        final InputChannel channel = createInputChannel(name);
+
+        try {
+            monitorToken.linkToDeath(() -> removeSpyWindowGestureMonitor(channel.getToken()), 0);
+        } catch (RemoteException e) {
+            Slog.i(TAG, "Client died before '" + name + "' could be created.");
+            return null;
+        }
+        synchronized (mInputMonitors) {
+            mInputMonitors.put(channel.getToken(),
+                    new GestureMonitorSpyWindow(monitorToken, name, displayId, pid, uid, sc,
+                            channel));
+        }
+
+        final InputChannel outInputChannel = new InputChannel();
+        channel.copyTo(outInputChannel);
+        return outInputChannel;
+    }
+
+    private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) {
+        final GestureMonitorSpyWindow monitor;
+        synchronized (mInputMonitors) {
+            monitor = mInputMonitors.remove(inputChannelToken);
+        }
+        removeInputChannel(inputChannelToken);
+        if (monitor == null) return;
+        monitor.remove();
+    }
+
     /**
      * Creates an input monitor that will receive pointer events for the purposes of system-wide
      * gesture interpretation.
      *
-     * @param inputChannelName The input channel name.
+     * @param requestedName The input channel name.
      * @param displayId Target display id.
      * @return The input channel.
      */
     @Override // Binder call
-    public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {
+    public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName,
+            int displayId) {
         if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
-                "monitorInputRegion()")) {
+                "monitorGestureInput()")) {
             throw new SecurityException("Requires MONITOR_INPUT permission");
         }
-        Objects.requireNonNull(inputChannelName, "inputChannelName must not be null.");
+        Objects.requireNonNull(requestedName, "name must not be null.");
+        Objects.requireNonNull(monitorToken, "token must not be null.");
 
         if (displayId < Display.DEFAULT_DISPLAY) {
             throw new IllegalArgumentException("displayId must >= 0.");
         }
+        final String name = "[Gesture Monitor] " + requestedName;
         final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            InputChannel inputChannel = nativeCreateInputMonitor(
-                    mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid);
-            InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
-            return new InputMonitor(inputChannel, host);
+            final InputChannel inputChannel =
+                    USE_SPY_WINDOW_GESTURE_MONITORS
+                            ? createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid)
+                            : nativeCreateInputMonitor(mPtr, displayId, true /*isGestureMonitor*/,
+                                    requestedName, pid);
+            return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken()));
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -2563,37 +2615,51 @@
         String dumpStr = nativeDump(mPtr);
         if (dumpStr != null) {
             pw.println(dumpStr);
-            dumpAssociations(pw);
         }
+
+        pw.println("Input Manager Service (Java) State:");
+        dumpAssociations(pw, "  " /*prefix*/);
+        dumpSpyWindowGestureMonitors(pw, "  " /*prefix*/);
     }
 
-    private void dumpAssociations(PrintWriter pw) {
+    private void dumpAssociations(PrintWriter pw, String prefix) {
         if (!mStaticAssociations.isEmpty()) {
-            pw.println("Static Associations:");
+            pw.println(prefix + "Static Associations:");
             mStaticAssociations.forEach((k, v) -> {
-                pw.print("  port: " + k);
+                pw.print(prefix + "  port: " + k);
                 pw.println("  display: " + v);
             });
         }
 
         synchronized (mAssociationsLock) {
             if (!mRuntimeAssociations.isEmpty()) {
-                pw.println("Runtime Associations:");
+                pw.println(prefix + "Runtime Associations:");
                 mRuntimeAssociations.forEach((k, v) -> {
-                    pw.print("  port: " + k);
+                    pw.print(prefix + "  port: " + k);
                     pw.println("  display: " + v);
                 });
             }
             if (!mUniqueIdAssociations.isEmpty()) {
-                pw.println("Unique Id Associations:");
+                pw.println(prefix + "Unique Id Associations:");
                 mUniqueIdAssociations.forEach((k, v) -> {
-                    pw.print("  port: " + k);
+                    pw.print(prefix + "  port: " + k);
                     pw.println("  uniqueId: " + v);
                 });
             }
         }
     }
 
+    private void dumpSpyWindowGestureMonitors(PrintWriter pw, String prefix) {
+        synchronized (mInputMonitors) {
+            if (mInputMonitors.isEmpty()) return;
+            pw.println(prefix + "Gesture Monitors (implemented as spy windows):");
+            int i = 0;
+            for (final GestureMonitorSpyWindow monitor : mInputMonitors.values()) {
+                pw.append(prefix + "  " + i++ + ": ").println(monitor.dump());
+            }
+        }
+    }
+
     private boolean checkCallingPermission(String permission, String func) {
         // Quick check: if the calling permission is me, it's all okay.
         if (Binder.getCallingPid() == Process.myPid()) {
@@ -2618,6 +2684,7 @@
         synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
         synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
         synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ }
+        synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }
         nativeMonitor(mPtr);
     }
 
@@ -2686,6 +2753,11 @@
 
     // Native callback.
     private void notifyInputChannelBroken(IBinder token) {
+        synchronized (mInputMonitors) {
+            if (mInputMonitors.containsKey(token)) {
+                removeSpyWindowGestureMonitor(token);
+            }
+        }
         mWindowManagerCallbacks.notifyInputChannelBroken(token);
     }
 
@@ -2721,6 +2793,17 @@
 
     // Native callback
     private void notifyWindowUnresponsive(IBinder token, String reason) {
+        int gestureMonitorPid = -1;
+        synchronized (mInputMonitors) {
+            final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
+            if (gestureMonitor != null) {
+                gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
+            }
+        }
+        if (gestureMonitorPid != -1) {
+            mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(gestureMonitorPid, reason);
+            return;
+        }
         mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);
     }
 
@@ -2731,6 +2814,17 @@
 
     // Native callback
     private void notifyWindowResponsive(IBinder token) {
+        int gestureMonitorPid = -1;
+        synchronized (mInputMonitors) {
+            final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
+            if (gestureMonitor != null) {
+                gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
+            }
+        }
+        if (gestureMonitorPid != -1) {
+            mWindowManagerCallbacks.notifyGestureMonitorResponsive(gestureMonitorPid);
+            return;
+        }
         mWindowManagerCallbacks.notifyWindowResponsive(token);
     }
 
@@ -3184,6 +3278,16 @@
          * pointers such as the mouse cursor and touch spots for the given display.
          */
         SurfaceControl getParentSurfaceForPointers(int displayId);
+
+        /**
+         * Create a {@link SurfaceControl} that can be configured to receive input over the entire
+         * display to implement a gesture monitor. The surface will not have a graphical buffer.
+         * @param name the name of the gesture monitor
+         * @param displayId the display to create the window in
+         * @return the SurfaceControl of the new layer container surface
+         */
+        @Nullable
+        SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);
     }
 
     /**
@@ -3258,20 +3362,24 @@
      * Interface for the system to handle request from InputMonitors.
      */
     private final class InputMonitorHost extends IInputMonitorHost.Stub {
-        private final IBinder mToken;
+        private final IBinder mInputChannelToken;
 
-        InputMonitorHost(IBinder token) {
-            mToken = token;
+        InputMonitorHost(IBinder inputChannelToken) {
+            mInputChannelToken = inputChannelToken;
         }
 
         @Override
         public void pilferPointers() {
-            nativePilferPointers(mPtr, mToken);
+            nativePilferPointers(mPtr, mInputChannelToken);
         }
 
         @Override
         public void dispose() {
-            nativeRemoveInputChannel(mPtr, mToken);
+            if (USE_SPY_WINDOW_GESTURE_MONITORS) {
+                removeSpyWindowGestureMonitor(mInputChannelToken);
+                return;
+            }
+            nativeRemoveInputChannel(mPtr, mInputChannelToken);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 4c26166..9846a2b 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -19,7 +19,6 @@
 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 
 import android.annotation.NonNull;
-import android.graphics.Rect;
 import android.os.Process;
 import android.view.InputApplicationHandle;
 import android.view.InputChannel;
@@ -33,8 +32,11 @@
     public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
     static final boolean DEBUG = HandwritingModeController.DEBUG;
 
-    private final int mClientPid;
-    private final int mClientUid;
+    // Place the layer below the highest layer to place it under gesture monitors. If the surface
+    // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface
+    // is intercepting.
+    // TODO(b/217538817): Specify the ordering in WM by usage.
+    private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1;
 
     private final InputApplicationHandle mApplicationHandle;
     private final InputWindowHandle mWindowHandle;
@@ -44,9 +46,6 @@
 
     HandwritingEventReceiverSurface(String name, int displayId, @NonNull SurfaceControl sc,
             @NonNull InputChannel inputChannel) {
-        // Initialized the window as being owned by the system.
-        mClientPid = Process.myPid();
-        mClientUid = Process.myUid();
         mApplicationHandle = new InputApplicationHandle(null, name,
                 DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
 
@@ -57,29 +56,26 @@
         mWindowHandle.name = name;
         mWindowHandle.token = mClientChannel.getToken();
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
-        mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+        mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
         mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         mWindowHandle.visible = true;
         mWindowHandle.focusable = false;
         mWindowHandle.hasWallpaper = false;
         mWindowHandle.paused = false;
-        mWindowHandle.ownerPid = mClientPid;
-        mWindowHandle.ownerUid = mClientUid;
+        mWindowHandle.ownerPid = Process.myPid();
+        mWindowHandle.ownerUid = Process.myUid();
         mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
                 | WindowManager.LayoutParams.INPUT_FEATURE_INTERCEPTS_STYLUS;
         mWindowHandle.scaleFactor = 1.0f;
         mWindowHandle.trustedOverlay = true;
-        mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface as crop */);
+        mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
-        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+        t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER);
         t.setPosition(mInputSurface, 0, 0);
-        // Use an arbitrarily large crop that is positioned at the origin. The crop determines the
-        // bounds and the coordinate space of the input events, so it must start at the origin to
-        // receive input in display space.
-        // TODO(b/210039666): fix this in SurfaceFlinger and avoid the hack.
-        t.setCrop(mInputSurface, new Rect(0, 0, 10000, 10000));
+        t.setCrop(mInputSurface, null /* crop to parent surface */);
         t.show(mInputSurface);
         t.apply();
 
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index 134fb96..01aee7b 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -31,13 +31,11 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.os.BestClock;
 import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.LocaleList;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -61,7 +59,6 @@
 import java.nio.charset.StandardCharsets;
 import java.time.Clock;
 import java.time.Duration;
-import java.time.ZoneOffset;
 import java.util.HashMap;
 
 /**
@@ -97,15 +94,10 @@
 
     LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
             PackageManagerInternal pmInternal) {
-        this(localeManagerService.mContext, localeManagerService, pmInternal, getDefaultClock(),
+        this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(),
                 new SparseArray<>());
     }
 
-    private static @NonNull Clock getDefaultClock() {
-        return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
-                Clock.systemUTC());
-    }
-
     @VisibleForTesting LocaleManagerBackupHelper(Context context,
             LocaleManagerService localeManagerService,
             PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
index 5036a6e..bfef978 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
@@ -63,16 +63,25 @@
     @Override
     protected boolean registerWithService(Void ignored,
             Collection<GnssListenerRegistration> registrations) {
-        if (D) {
-            Log.d(TAG, "starting gnss nmea messages");
+        if (mGnssNative.startNmeaMessageCollection()) {
+            if (D) {
+                Log.d(TAG, "starting gnss nmea messages collection");
+            }
+            return true;
+        } else {
+            Log.e(TAG, "error starting gnss nmea messages collection");
+            return false;
         }
-        return true;
     }
 
     @Override
     protected void unregisterWithService() {
-        if (D) {
-            Log.d(TAG, "stopping gnss nmea messages");
+        if (mGnssNative.stopNmeaMessageCollection()) {
+            if (D) {
+                Log.d(TAG, "stopping gnss nmea messages collection");
+            }
+        } else {
+            Log.e(TAG, "error stopping gnss nmea messages collection");
         }
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 936283d..0ce36d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -43,6 +43,7 @@
 
     private final AppOpsHelper mAppOpsHelper;
     private final LocationUsageLogger mLogger;
+    private final GnssNative mGnssNative;
 
     private boolean mIsNavigating = false;
 
@@ -50,6 +51,7 @@
         super(injector);
         mAppOpsHelper = injector.getAppOpsHelper();
         mLogger = injector.getLocationUsageLogger();
+        mGnssNative = gnssNative;
 
         gnssNative.addBaseCallbacks(this);
         gnssNative.addStatusCallbacks(this);
@@ -64,16 +66,25 @@
     @Override
     protected boolean registerWithService(Void ignored,
             Collection<GnssListenerRegistration> registrations) {
-        if (D) {
-            Log.d(TAG, "starting gnss status");
+        if (mGnssNative.startSvStatusCollection()) {
+            if (D) {
+                Log.d(TAG, "starting gnss sv status");
+            }
+            return true;
+        } else {
+            Log.e(TAG, "error starting gnss sv status");
+            return false;
         }
-        return true;
     }
 
     @Override
     protected void unregisterWithService() {
-        if (D) {
-            Log.d(TAG, "stopping gnss status");
+        if (mGnssNative.stopSvStatusCollection()) {
+            if (D) {
+                Log.d(TAG, "stopping gnss sv status");
+            }
+        } else {
+            Log.e(TAG, "error stopping gnss sv status");
         }
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index e072bf7..af87677 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -783,6 +783,38 @@
     }
 
     /**
+     * Starts sv status collection.
+     */
+    public boolean startSvStatusCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.startSvStatusCollection();
+    }
+
+    /**
+     * Stops sv status collection.
+     */
+    public boolean stopSvStatusCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.stopSvStatusCollection();
+    }
+
+    /**
+     * Starts NMEA message collection.
+     */
+    public boolean startNmeaMessageCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.startNmeaMessageCollection();
+    }
+
+    /**
+     * Stops NMEA message collection.
+     */
+    public boolean stopNmeaMessageCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.stopNmeaMessageCollection();
+    }
+
+    /**
      * Returns true if measurement corrections are supported.
      */
     public boolean isMeasurementCorrectionsSupported() {
@@ -1369,6 +1401,22 @@
             return native_inject_measurement_corrections(corrections);
         }
 
+        protected boolean startSvStatusCollection() {
+            return native_start_sv_status_collection();
+        }
+
+        protected boolean stopSvStatusCollection() {
+            return native_stop_sv_status_collection();
+        }
+
+        protected boolean startNmeaMessageCollection() {
+            return native_start_nmea_message_collection();
+        }
+
+        protected boolean stopNmeaMessageCollection() {
+            return native_stop_nmea_message_collection();
+        }
+
         protected int getBatchSize() {
             return native_get_batch_size();
         }
@@ -1478,6 +1526,10 @@
 
     private static native int native_read_nmea(byte[] buffer, int bufferSize);
 
+    private static native boolean native_start_nmea_message_collection();
+
+    private static native boolean native_stop_nmea_message_collection();
+
     // location injection APIs
 
     private static native void native_inject_location(
@@ -1501,6 +1553,11 @@
 
     private static native void native_inject_time(long time, long timeReference, int uncertainty);
 
+    // sv status APIs
+    private static native boolean native_start_sv_status_collection();
+
+    private static native boolean native_stop_sv_status_collection();
+
     // navigation message APIs
 
     private static native boolean native_is_navigation_message_supported();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 96d7521..70e968f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -201,6 +201,10 @@
     private boolean mIsAppImportanceLocked;
     private ArraySet<Uri> mGrantableUris;
 
+    // Storage for phone numbers that were found to be associated with
+    // contacts in this notification.
+    private ArraySet<String> mPhoneNumbers;
+
     // Whether this notification record should have an update logged the next time notifications
     // are sorted.
     private boolean mPendingLogUpdate = false;
@@ -1547,6 +1551,26 @@
         return mPendingLogUpdate;
     }
 
+    /**
+     * Merge the given set of phone numbers into the list of phone numbers that
+     * are cached on this notification record.
+     */
+    public void mergePhoneNumbers(ArraySet<String> phoneNumbers) {
+        // if the given phone numbers are null or empty then don't do anything
+        if (phoneNumbers == null || phoneNumbers.size() == 0) {
+            return;
+        }
+        // initialize if not already
+        if (mPhoneNumbers == null) {
+            mPhoneNumbers = new ArraySet<>();
+        }
+        mPhoneNumbers.addAll(phoneNumbers);
+    }
+
+    public ArraySet<String> getPhoneNumbers() {
+        return mPhoneNumbers;
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index d7bc3bb..dc4d04f 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -68,7 +68,10 @@
     private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
     private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
             "validate_notification_people_enabled";
-    private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED };
+    private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.LOOKUP_KEY,
+            Contacts.STARRED, Contacts.HAS_PHONE_NUMBER };
+    private static final String[] PHONE_LOOKUP_PROJECTION =
+            { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER };
     private static final int MAX_PEOPLE = 10;
     private static final int PEOPLE_CACHE_SIZE = 200;
 
@@ -409,6 +412,35 @@
         return lookupResult;
     }
 
+    @VisibleForTesting
+    // Performs a contacts search using searchContacts, and then follows up by looking up
+    // any phone numbers associated with the resulting contact information and merge those
+    // into the lookup result as well. Will have no additional effect if the contact does
+    // not have any phone numbers.
+    LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) {
+        LookupResult lookupResult = searchContacts(context, lookupUri);
+        String phoneLookupKey = lookupResult.getPhoneLookupKey();
+        if (phoneLookupKey != null) {
+            String selection = Contacts.LOOKUP_KEY + " = ?";
+            String[] selectionArgs = new String[] { phoneLookupKey };
+            try (Cursor cursor = context.getContentResolver().query(
+                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION,
+                    selection, selectionArgs, /* sortOrder= */ null)) {
+                if (cursor == null) {
+                    Slog.w(TAG, "Cursor is null when querying contact phone number.");
+                    return lookupResult;
+                }
+
+                while (cursor.moveToNext()) {
+                    lookupResult.mergePhoneNumber(cursor);
+                }
+            } catch (Throwable t) {
+                Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t);
+            }
+        }
+        return lookupResult;
+    }
+
     private void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) {
         final int workUserId = findWorkUserId(context);
         if (workUserId == -1) {
@@ -454,6 +486,9 @@
 
         private final long mExpireMillis;
         private float mAffinity = NONE;
+        private boolean mHasPhone = false;
+        private String mPhoneLookupKey = null;
+        private ArraySet<String> mPhoneNumbers = new ArraySet<>();
 
         public LookupResult() {
             mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
@@ -473,6 +508,15 @@
                 Slog.i(TAG, "invalid cursor: no _ID");
             }
 
+            // Lookup key for potentially looking up contact phone number later
+            final int lookupKeyIdx = cursor.getColumnIndex(Contacts.LOOKUP_KEY);
+            if (lookupKeyIdx >= 0) {
+                mPhoneLookupKey = cursor.getString(lookupKeyIdx);
+                if (DEBUG) Slog.d(TAG, "contact LOOKUP_KEY is: " + mPhoneLookupKey);
+            } else {
+                if (DEBUG) Slog.d(TAG, "invalid cursor: no LOOKUP_KEY");
+            }
+
             // Starred
             final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
             if (starIdx >= 0) {
@@ -484,6 +528,39 @@
             } else {
                 if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
             }
+
+            // whether a phone number is present
+            final int hasPhoneIdx = cursor.getColumnIndex(Contacts.HAS_PHONE_NUMBER);
+            if (hasPhoneIdx >= 0) {
+                mHasPhone = cursor.getInt(hasPhoneIdx) != 0;
+                if (DEBUG) Slog.d(TAG, "contact HAS_PHONE_NUMBER is: " + mHasPhone);
+            } else {
+                if (DEBUG) Slog.d(TAG, "invalid cursor: no HAS_PHONE_NUMBER");
+            }
+        }
+
+        // Returns the phone lookup key that is cached in this result, or null
+        // if the contact has no known phone info.
+        public String getPhoneLookupKey() {
+            if (!mHasPhone) {
+                return null;
+            }
+            return mPhoneLookupKey;
+        }
+
+        // Merge phone number found in this lookup and store it in mPhoneNumbers.
+        public void mergePhoneNumber(Cursor cursor) {
+            final int phoneNumIdx = cursor.getColumnIndex(
+                    ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER);
+            if (phoneNumIdx >= 0) {
+                mPhoneNumbers.add(cursor.getString(phoneNumIdx));
+            } else {
+                if (DEBUG) Slog.d(TAG, "invalid cursor: no NORMALIZED_NUMBER");
+            }
+        }
+
+        public ArraySet<String> getPhoneNumbers() {
+            return mPhoneNumbers;
         }
 
         private boolean isExpired() {
@@ -509,6 +586,7 @@
         // Amount of time to wait for a result from the contacts db before rechecking affinity.
         private static final long LOOKUP_TIME = 1000;
         private float mContactAffinity = NONE;
+        private ArraySet<String> mPhoneNumbers = null;
         private NotificationRecord mRecord;
 
         private PeopleRankingReconsideration(Context context, String key,
@@ -543,7 +621,9 @@
                         lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
                     } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
                         if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
-                        lookupResult = searchContacts(mContext, uri);
+                        // only look up phone number if this is a contact lookup uri and thus isn't
+                        // already directly a phone number.
+                        lookupResult = searchContactsAndLookupNumbers(mContext, uri);
                     } else {
                         lookupResult = new LookupResult();  // invalid person for the cache
                         if (!"name".equals(uri.getScheme())) {
@@ -561,6 +641,13 @@
                         Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
                     }
                     mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
+                    // merge any phone numbers found in this lookup result
+                    if (lookupResult.getPhoneNumbers() != null) {
+                        if (mPhoneNumbers == null) {
+                            mPhoneNumbers = new ArraySet<>();
+                        }
+                        mPhoneNumbers.addAll(lookupResult.getPhoneNumbers());
+                    }
                 } else {
                     if (DEBUG) Slog.d(TAG, "lookupResult is null");
                 }
@@ -581,6 +668,7 @@
             float affinityBound = operand.getContactAffinity();
             operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
             if (VERBOSE) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
+            operand.mergePhoneNumbers(mPhoneNumbers);
         }
 
         public float getContactAffinity() {
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 29aad63..d04b331 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -33,6 +33,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.messages.nano.SystemMessageProto;
@@ -140,7 +141,7 @@
     }
 
     protected void recordCall(NotificationRecord record) {
-        REPEAT_CALLERS.recordCall(mContext, extras(record));
+        REPEAT_CALLERS.recordCall(mContext, extras(record), record.getPhoneNumbers());
     }
 
     /**
@@ -351,7 +352,8 @@
         private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>();
         private int mThresholdMinutes;
 
-        private synchronized void recordCall(Context context, Bundle extras) {
+        private synchronized void recordCall(Context context, Bundle extras,
+                ArraySet<String> phoneNumbers) {
             setThresholdMinutes(context);
             if (mThresholdMinutes <= 0 || extras == null) return;
             final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
@@ -359,7 +361,7 @@
             final long now = System.currentTimeMillis();
             cleanUp(mTelCalls, now);
             cleanUp(mOtherCalls, now);
-            recordCallers(extraPeople, now);
+            recordCallers(extraPeople, phoneNumbers, now);
         }
 
         private synchronized boolean isRepeat(Context context, Bundle extras) {
@@ -407,7 +409,8 @@
             }
         }
 
-        private synchronized void recordCallers(String[] people, long now) {
+        private synchronized void recordCallers(String[] people, ArraySet<String> phoneNumbers,
+                long now) {
             for (int i = 0; i < people.length; i++) {
                 String person = people[i];
                 if (person == null) continue;
@@ -428,6 +431,14 @@
                     mOtherCalls.put(person, now);
                 }
             }
+
+            // record any additional numbers from the notification record if
+            // provided; these are in the format of just a phone number string
+            if (phoneNumbers != null) {
+                for (String num : phoneNumbers) {
+                    mTelCalls.put(num, now);
+                }
+            }
         }
 
         private synchronized boolean checkCallers(Context context, String[] people) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3549c9e..ee5c638 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -247,6 +247,7 @@
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
 import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.supplementalprocess.SupplementalProcessManagerLocal;
 import com.android.server.utils.SnapshotCache;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.utils.Watchable;
@@ -934,6 +935,7 @@
     final @Nullable String mOverlayConfigSignaturePackage;
     final @Nullable String mRecentsPackage;
     final @Nullable String mAmbientContextDetectionPackage;
+    private final @NonNull String mRequiredSupplementalProcessPackage;
 
     @GuardedBy("mLock")
     private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1671,6 +1673,7 @@
         mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
         mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
         mResolveComponentName = testParams.resolveComponentName;
+        mRequiredSupplementalProcessPackage = testParams.requiredSupplementalProcessPackage;
 
         mLiveComputer = createLiveComputer();
         mSnapshotComputer = null;
@@ -1695,6 +1698,7 @@
         mResolveIntentHelper = testParams.resolveIntentHelper;
         mDexOptHelper = testParams.dexOptHelper;
         mSuspendPackageHelper = testParams.suspendPackageHelper;
+
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
 
         registerObservers(false);
@@ -2138,6 +2142,9 @@
                     getPackageInfo(mRequiredPermissionControllerPackage, 0,
                             UserHandle.USER_SYSTEM).getLongVersionCode());
 
+            // Resolve the supplemental process
+            mRequiredSupplementalProcessPackage = getRequiredSupplementalProcessPackageName();
+
             // Initialize InstantAppRegistry's Instant App list for all users.
             for (AndroidPackage pkg : mPackages.values()) {
                 if (pkg.isSystem()) {
@@ -3153,6 +3160,11 @@
         throw new IllegalStateException("PermissionController is not found");
     }
 
+    @Override
+    public String getSupplementalProcessPackageName() {
+        return mRequiredSupplementalProcessPackage;
+    }
+
     String getPackageInstallerPackageName() {
         return mRequiredInstallerPackage;
     }
@@ -5518,6 +5530,24 @@
         }
     }
 
+    private @NonNull String getRequiredSupplementalProcessPackageName() {
+        final Intent intent = new Intent(SupplementalProcessManagerLocal.SERVICE_INTERFACE);
+
+        final List<ResolveInfo> matches = queryIntentServicesInternal(
+                intent,
+                /* resolvedType= */ null,
+                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                UserHandle.USER_SYSTEM,
+                /* callingUid= */ Process.myUid(),
+                /* includeInstantApps= */ false);
+        if (matches.size() == 1) {
+            return matches.get(0).getComponentInfo().packageName;
+        } else {
+            throw new RuntimeException("There should exactly one supplemental process; found "
+                    + matches.size() + ": matches=" + matches);
+        }
+    }
+
     @Override
     public String getDefaultTextClassifierPackageName() {
         return ensureSystemPackageName(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 1caa76d..d12c826 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -89,6 +89,7 @@
     public @Nullable String defaultTextClassifierPackage;
     public @Nullable String systemTextClassifierPackage;
     public @Nullable String overlayConfigSignaturePackage;
+    public @NonNull String requiredSupplementalProcessPackage;
     public ViewCompiler viewCompiler;
     public @Nullable String retailDemoPackage;
     public @Nullable String recentsPackage;
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 79c5ea2..edc0e3d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -51,6 +51,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -613,11 +614,12 @@
             int granted = PermissionManagerService.this.checkUidPermission(uid,
                     POST_NOTIFICATIONS);
             AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
-            if (granted != PermissionManager.PERMISSION_GRANTED) {
+            if (granted != PackageManager.PERMISSION_GRANTED
+                    && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
                 int flags = PermissionManagerService.this.getPermissionFlags(pkg.getPackageName(),
                         POST_NOTIFICATIONS, UserHandle.getUserId(uid));
                 if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                    return PermissionManager.PERMISSION_GRANTED;
+                    return PackageManager.PERMISSION_GRANTED;
                 }
             }
             return granted;
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
index f519ced..243efb5 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.security;
 
+import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
 import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
 
 import android.content.Context;
@@ -76,10 +78,24 @@
     private void verifyAttestationForAllVerifiers(
             AttestationProfile profile, int localBindingType, Bundle requirements,
             byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) {
-        // TODO(b/201696614): Implement
         IVerificationResult result = new IVerificationResult();
-        result.resultCode = RESULT_UNKNOWN;
+        // TODO(b/201696614): Implement
         result.token = null;
+        switch (profile.getAttestationProfileId()) {
+            case PROFILE_SELF_TRUSTED:
+                Slog.d(TAG, "Verifying Self trusted profile.");
+                try {
+                    result.resultCode =
+                            AttestationVerificationSelfTrustedVerifierForTesting.getInstance()
+                                    .verifyAttestation(localBindingType, requirements, attestation);
+                } catch (Throwable t) {
+                    result.resultCode = RESULT_FAILURE;
+                }
+                break;
+            default:
+                Slog.d(TAG, "No profile found, defaulting.");
+                result.resultCode = RESULT_UNKNOWN;
+        }
         resultCallback.complete(result);
     }
 
diff --git a/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
new file mode 100644
index 0000000..58df2bd
--- /dev/null
+++ b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security;
+
+import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
+import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Bundle;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Verifies {@code PROFILE_SELF_TRUSTED} attestations.
+ *
+ * Verifies that the attesting environment can create an attestation with the same root certificate
+ * as the verifying device with a matching attestation challenge. Skips CRL revocations checking
+ * so this verifier can work in a hermetic test environment.
+ *
+ * This verifier profile is intended to be used only for testing.
+ */
+class AttestationVerificationSelfTrustedVerifierForTesting {
+    private static final String TAG = "AVF";
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
+
+    // The OID for the extension Android Keymint puts into device-generated certificates.
+    private static final String ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID =
+            "1.3.6.1.4.1.11129.2.1.17";
+
+    // ASN.1 sequence index values for the Android Keymint extension.
+    private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+
+    private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
+    private static final String GOLDEN_ALIAS =
+            AttestationVerificationSelfTrustedVerifierForTesting.class.getCanonicalName()
+                    + ".Golden";
+
+    private static volatile AttestationVerificationSelfTrustedVerifierForTesting
+            sAttestationVerificationSelfTrustedVerifier = null;
+
+    private final CertificateFactory mCertificateFactory;
+    private final CertPathValidator mCertPathValidator;
+    private final KeyStore mAndroidKeyStore;
+    private X509Certificate mGoldenRootCert;
+
+    static AttestationVerificationSelfTrustedVerifierForTesting getInstance()
+            throws Exception {
+        if (sAttestationVerificationSelfTrustedVerifier == null) {
+            synchronized (AttestationVerificationSelfTrustedVerifierForTesting.class) {
+                if (sAttestationVerificationSelfTrustedVerifier == null) {
+                    sAttestationVerificationSelfTrustedVerifier =
+                            new AttestationVerificationSelfTrustedVerifierForTesting();
+                }
+            }
+        }
+        return sAttestationVerificationSelfTrustedVerifier;
+    }
+
+    private static void debugVerboseLog(String str, Throwable t) {
+        if (DEBUG) {
+            Slog.v(TAG, str, t);
+        }
+    }
+
+    private static void debugVerboseLog(String str) {
+        if (DEBUG) {
+            Slog.v(TAG, str);
+        }
+    }
+
+    private AttestationVerificationSelfTrustedVerifierForTesting() throws Exception {
+        mCertificateFactory = CertificateFactory.getInstance("X.509");
+        mCertPathValidator = CertPathValidator.getInstance("PKIX");
+        mAndroidKeyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
+        mAndroidKeyStore.load(null);
+        if (!mAndroidKeyStore.containsAlias(GOLDEN_ALIAS)) {
+            KeyPairGenerator kpg =
+                    KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
+            KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder(
+                    GOLDEN_ALIAS, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                    .setAttestationChallenge(GOLDEN_ALIAS.getBytes())
+                    .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512).build();
+            kpg.initialize(parameterSpec);
+            kpg.generateKeyPair();
+        }
+
+        X509Certificate[] goldenCerts = (X509Certificate[])
+                ((KeyStore.PrivateKeyEntry) mAndroidKeyStore.getEntry(GOLDEN_ALIAS, null))
+                        .getCertificateChain();
+        mGoldenRootCert = goldenCerts[goldenCerts.length - 1];
+    }
+
+    int verifyAttestation(
+            int localBindingType, @NonNull Bundle requirements,  @NonNull byte[] attestation) {
+        List<X509Certificate> certificates = new ArrayList<>();
+        ByteArrayInputStream bis = new ByteArrayInputStream(attestation);
+        try {
+            while (bis.available() > 0) {
+                certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
+            }
+        } catch (CertificateException e) {
+            debugVerboseLog("Unable to parse certificates from attestation", e);
+            return RESULT_FAILURE;
+        }
+
+        if (localBindingType == TYPE_CHALLENGE
+                && validateRequirements(requirements)
+                && checkLeafChallenge(requirements, certificates)
+                && verifyCertificateChain(certificates)) {
+            return RESULT_SUCCESS;
+        }
+
+        return RESULT_FAILURE;
+    }
+
+    private boolean verifyCertificateChain(List<X509Certificate> certificates) {
+        if (certificates.size() < 2) {
+            debugVerboseLog("Certificate chain less than 2 in size.");
+            return false;
+        }
+
+        try {
+            CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
+            PKIXParameters validationParams = new PKIXParameters(getTrustAnchors());
+            // Skipping revocation checking because we want this to work in a hermetic test
+            // environment.
+            validationParams.setRevocationEnabled(false);
+            mCertPathValidator.validate(certificatePath, validationParams);
+        } catch (Throwable t) {
+            debugVerboseLog("Invalid certificate chain", t);
+            return false;
+        }
+
+        return true;
+    }
+
+    private Set<TrustAnchor> getTrustAnchors() {
+        return Collections.singleton(new TrustAnchor(mGoldenRootCert, null));
+    }
+
+    private boolean validateRequirements(Bundle requirements) {
+        if (requirements.size() != 1) {
+            debugVerboseLog("Requirements does not contain exactly 1 key.");
+            return false;
+        }
+        if (!requirements.containsKey(PARAM_CHALLENGE)) {
+            debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates) {
+        // Verify challenge
+        byte[] challenge;
+        try {
+            challenge = getChallengeFromCert(certificates.get(0));
+        } catch (Throwable t) {
+            debugVerboseLog("Unable to parse challenge from certificate.", t);
+            return false;
+        }
+
+        if (Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) {
+            return true;
+        } else {
+            debugVerboseLog("Self-Trusted validation failed; challenge mismatch.");
+            return false;
+        }
+    }
+
+    private byte[] getChallengeFromCert(@NonNull X509Certificate x509Certificate)
+            throws CertificateEncodingException, IOException {
+        Certificate certificate = Certificate.getInstance(
+                new ASN1InputStream(x509Certificate.getEncoded()).readObject());
+        ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
+                .getExtensionParsedValue(
+                        new ASN1ObjectIdentifier(ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID));
+        return ((ASN1OctetString) keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX))
+                .getOctets();
+    }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8a87c96..94f483c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -53,6 +53,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.media.MediaRoute2Info;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -92,6 +93,7 @@
 import com.android.internal.statusbar.ISessionListener;
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.RegisterStatusBarResult;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -1265,6 +1267,12 @@
                 "StatusBarManagerService");
     }
 
+    private void enforceMediaContentControl() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MEDIA_CONTENT_CONTROL,
+                "StatusBarManagerService");
+    }
+
     /**
      *  For targetSdk S+ we require STATUS_BAR. For targetSdk < S, we only require EXPAND_STATUS_BAR
      *  but also require that it falls into one of the allowed use-cases to lock down abuse vector.
@@ -1987,6 +1995,53 @@
         return false;
     }
 
+    /**
+     * Notifies the system of a new media tap-to-transfer state for the *sender* device. See
+     * {@link StatusBarManager.updateMediaTapToTransferSenderDisplay} for more information.
+     *
+     * @param undoCallback a callback that will be triggered if the user elects to undo a media
+     *                     transfer.
+     *
+     * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL}
+     * permission.
+     */
+    @Override
+    public void updateMediaTapToTransferSenderDisplay(
+            @StatusBarManager.MediaTransferSenderState int displayState,
+            @NonNull MediaRoute2Info routeInfo,
+            @Nullable IUndoMediaTransferCallback undoCallback
+    ) {
+        enforceMediaContentControl();
+        if (mBar != null) {
+            try {
+                mBar.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, undoCallback);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "updateMediaTapToTransferSenderDisplay", e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the system of a new media tap-to-transfer state for the *receiver* device. See
+     * {@link StatusBarManager.updateMediaTapToTransferReceiverDisplay} for more information.
+     *
+     * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL}
+     * permission.
+     */
+    @Override
+    public void updateMediaTapToTransferReceiverDisplay(
+            @StatusBarManager.MediaTransferReceiverState int displayState,
+            MediaRoute2Info routeInfo) {
+        enforceMediaContentControl();
+        if (mBar != null) {
+            try {
+                mBar.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "updateMediaTapToTransferReceiverDisplay", e);
+            }
+        }
+    }
+
     /** @hide */
     public void passThroughShellCommand(String[] args, FileDescriptor fd) {
         enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 06ce4a4..1dea3d7 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -71,6 +71,7 @@
     private static final int MSG_ESCROW_TOKEN_STATE = 9;
     private static final int MSG_UNLOCK_USER = 10;
     private static final int MSG_SHOW_KEYGUARD_ERROR_MESSAGE = 11;
+    private static final int MSG_LOCK_USER = 12;
 
     /**
      * Time in uptime millis that we wait for the service connection, both when starting
@@ -100,6 +101,8 @@
 
     // Trust state
     private boolean mTrusted;
+    private boolean mWaitingForTrustableDowngrade = false;
+    private boolean mTrustable;
     private CharSequence mMessage;
     private boolean mDisplayTrustGrantedMessage;
     private boolean mTrustDisabledByDpm;
@@ -108,6 +111,25 @@
     private AlarmManager mAlarmManager;
     private final Intent mAlarmIntent;
     private PendingIntent mAlarmPendingIntent;
+    private final BroadcastReceiver mTrustableDowngradeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!TrustManagerService.ENABLE_ACTIVE_UNLOCK_FLAG) {
+                return;
+            }
+            if (!mWaitingForTrustableDowngrade) {
+                return;
+            }
+            // are these the broadcasts we want to listen to
+            if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())
+                    || Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
+                mTrusted = false;
+                mTrustable = true;
+                mWaitingForTrustableDowngrade = false;
+                mTrustManagerService.updateTrust(mUserId, 0);
+            }
+        }
+    };
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -126,16 +148,21 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_GRANT_TRUST:
-                    // TODO(b/213631675): Respect FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
                     if (!isConnected()) {
                         Log.w(TAG, "Agent is not connected, cannot grant trust: "
                                 + mName.flattenToShortString());
                         return;
                     }
                     mTrusted = true;
+                    mTrustable = false;
                     mMessage = (CharSequence) msg.obj;
                     int flags = msg.arg1;
                     mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
+                    if ((flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
+                        mWaitingForTrustableDowngrade = true;
+                    } else {
+                        mWaitingForTrustableDowngrade = false;
+                    }
                     long durationMs = msg.getData().getLong(DATA_DURATION);
                     if (durationMs > 0) {
                         final long duration;
@@ -270,6 +297,13 @@
                     mTrustManagerService.showKeyguardErrorMessage(message);
                     break;
                 }
+                case MSG_LOCK_USER: {
+                    mTrusted = false;
+                    mTrustable = false;
+                    mTrustManagerService.updateTrust(mUserId, 0 /* flags */);
+                    mTrustManagerService.lockUser(mUserId);
+                    break;
+                }
             }
         }
     };
@@ -295,6 +329,11 @@
         }
 
         @Override
+        public void lockUser() {
+            mHandler.sendEmptyMessage(MSG_LOCK_USER);
+        }
+
+        @Override
         public void setManagingTrust(boolean managingTrust) {
             if (DEBUG) Slog.d(TAG, "managingTrust()");
             mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget();
@@ -427,6 +466,9 @@
         final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
         alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
 
+        IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+        trustableFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
         // Schedules a restart for when connecting times out. If the connection succeeds,
         // the restart is canceled in mCallback's onConnected.
         scheduleRestart();
@@ -435,6 +477,7 @@
         if (mBound) {
             mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null,
                     Context.RECEIVER_EXPORTED);
+            mContext.registerReceiver(mTrustableDowngradeReceiver, trustableFilter);
         } else {
             Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
         }
@@ -591,6 +634,10 @@
         return mTrusted && mManagingTrust && !mTrustDisabledByDpm;
     }
 
+    public boolean isTrustable() {
+        return mTrustable && mManagingTrust && !mTrustDisabledByDpm;
+    }
+
     public boolean isManagingTrust() {
         return mManagingTrust && !mTrustDisabledByDpm;
     }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9bed24d..6aafd4a 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -54,6 +54,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -65,6 +66,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.IWindowManager;
@@ -145,6 +147,21 @@
     @GuardedBy("mUserIsTrusted")
     private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
 
+    //TODO(b/215724686): remove flag
+    public static final boolean ENABLE_ACTIVE_UNLOCK_FLAG = SystemProperties.getBoolean(
+            "fw.enable_active_unlock_flag", true);
+
+    private enum TrustState {
+        UNTRUSTED, // the phone is not unlocked by any trustagents
+        TRUSTABLE, // the phone is in a semi-locked state that can be unlocked if
+        // FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE is passed and a trustagent is trusted
+        TRUSTED // the phone is unlocked
+    };
+
+    @GuardedBy("mUserTrustState")
+    private final SparseArray<TrustManagerService.TrustState> mUserTrustState =
+            new SparseArray<>();
+
     /**
      * Stores the locked state for users on the device. There are three different type of users
      * which are handled slightly differently:
@@ -228,7 +245,6 @@
     }
 
     // Extend unlock config and logic
-
     private final class SettingsObserver extends ContentObserver {
         private final Uri TRUST_AGENTS_EXTEND_UNLOCK =
                 Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK);
@@ -396,6 +412,14 @@
     }
 
     private void updateTrust(int userId, int flags, boolean isFromUnlock) {
+        if (ENABLE_ACTIVE_UNLOCK_FLAG) {
+            updateTrustWithRenewableUnlock(userId, flags, isFromUnlock);
+        } else {
+            updateTrustWithExtendUnlock(userId, flags, isFromUnlock);
+        }
+    }
+
+    private void updateTrustWithExtendUnlock(int userId, int flags, boolean isFromUnlock) {
         boolean managed = aggregateIsTrustManaged(userId);
         dispatchOnTrustManagedChanged(managed, userId);
         if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@@ -441,6 +465,65 @@
         }
     }
 
+    private void updateTrustWithRenewableUnlock(int userId, int flags, boolean isFromUnlock) {
+        boolean managed = aggregateIsTrustManaged(userId);
+        dispatchOnTrustManagedChanged(managed, userId);
+        if (mStrongAuthTracker.isTrustAllowedForUser(userId)
+                && isTrustUsuallyManagedInternal(userId) != managed) {
+            updateTrustUsuallyManaged(userId, managed);
+        }
+
+        boolean trustedByAtLeastOneAgent = aggregateIsTrusted(userId);
+        boolean trustableByAtLeastOneAgent = aggregateIsTrustable(userId);
+        boolean wasTrusted;
+        boolean wasTrustable;
+        TrustState pendingTrustState;
+
+        IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+        boolean alreadyUnlocked = false;
+        try {
+            alreadyUnlocked = !wm.isKeyguardLocked();
+        } catch (RemoteException e) {
+        }
+
+        synchronized (mUserTrustState) {
+            wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED);
+            wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE);
+            boolean renewingTrust = wasTrustable && (
+                    (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0);
+            boolean canMoveToTrusted = alreadyUnlocked || isFromUnlock || renewingTrust;
+            boolean upgradingTrustForCurrentUser = (userId == mCurrentUser);
+
+            if (trustedByAtLeastOneAgent && wasTrusted) {
+                // no change
+                return;
+            } else if (trustedByAtLeastOneAgent && canMoveToTrusted
+                    && upgradingTrustForCurrentUser) {
+                pendingTrustState = TrustState.TRUSTED;
+            } else if (trustableByAtLeastOneAgent && (wasTrusted || wasTrustable)
+                    && upgradingTrustForCurrentUser) {
+                pendingTrustState = TrustState.TRUSTABLE;
+            } else {
+                pendingTrustState = TrustState.UNTRUSTED;
+            }
+
+            mUserTrustState.put(userId, pendingTrustState);
+        }
+        if (DEBUG) Slog.d(TAG, "pendingTrustState: " + pendingTrustState);
+
+        boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED;
+        dispatchOnTrustChanged(isNowTrusted, userId, flags, getTrustGrantedMessages(userId));
+        if (isNowTrusted != wasTrusted) {
+            refreshDeviceLockedForUser(userId);
+            if (!isNowTrusted) {
+                maybeLockScreen(userId);
+            } else {
+                scheduleTrustTimeout(userId, false /* override */);
+            }
+        }
+    }
+
+
     private void updateTrustUsuallyManaged(int userId, boolean managed) {
         synchronized (mTrustUsuallyManagedForUser) {
             mTrustUsuallyManagedForUser.put(userId, managed);
@@ -472,6 +555,20 @@
         mLockPatternUtils.unlockUserWithToken(handle, token, userId);
     }
 
+    /**
+     * Locks the phone and requires some auth (not trust) like a biometric or passcode before
+     * unlocking.
+     */
+    public void lockUser(int userId) {
+        mLockPatternUtils.requireStrongAuth(
+                StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+        try {
+            WindowManagerGlobal.getWindowManagerService().lockNow(null);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error locking screen when called from trust agent");
+        }
+    }
+
     void showKeyguardErrorMessage(CharSequence message) {
         dispatchOnTrustError(message);
     }
@@ -950,6 +1047,21 @@
         return false;
     }
 
+    private boolean aggregateIsTrustable(int userId) {
+        if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+            return false;
+        }
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == userId) {
+                if (info.agent.isTrustable()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private List<String> getTrustGrantedMessages(int userId) {
         if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
             return new ArrayList<>();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f91969b..1f0fdcf 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -24,6 +24,7 @@
 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.PointF;
 import android.os.Debug;
 import android.os.IBinder;
@@ -254,6 +255,25 @@
         }
     }
 
+    @Override
+    @Nullable
+    public SurfaceControl createSurfaceForGestureMonitor(String name, int displayId) {
+        synchronized (mService.mGlobalLock) {
+            final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+            if (dc == null) {
+                Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+                        + " - DisplayContent not found.");
+                return null;
+            }
+            return mService.makeSurfaceBuilder(dc.getSession())
+                    .setContainerLayer()
+                    .setName(name)
+                    .setCallsite("createSurfaceForGestureMonitor")
+                    .setParent(dc.getSurfaceControl())
+                    .build();
+        }
+    }
+
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 10776ab..1e12173 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -32,6 +32,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.app.StatusBarManager;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -134,7 +135,7 @@
 
     /** Updates the target which can control system bars. */
     void updateBarControlTarget(@Nullable WindowState focusedWin) {
-        if (mFocusedWin != focusedWin){
+        if (mFocusedWin != focusedWin) {
             abortTransient();
         }
         mFocusedWin = focusedWin;
@@ -156,7 +157,7 @@
     }
 
     boolean isHidden(@InternalInsetsType int type) {
-        final InsetsSourceProvider provider =  mStateController.peekSourceProvider(type);
+        final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
         return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
     }
 
@@ -181,6 +182,10 @@
                         mShowingTransientTypes.toArray(), isGestureOnSystemBar);
             }
             updateBarControlTarget(mFocusedWin);
+            dispatchTransientSystemBarsVisibilityChanged(
+                    mFocusedWin,
+                    isTransient(ITYPE_STATUS_BAR) || isTransient(ITYPE_NAVIGATION_BAR),
+                    isGestureOnSystemBar);
 
             // The leashes can be created while updating bar control target. The surface transaction
             // of the new leashes might not be applied yet. The callback posted here ensures we can
@@ -198,6 +203,12 @@
         if (mShowingTransientTypes.size() == 0) {
             return;
         }
+
+        dispatchTransientSystemBarsVisibilityChanged(
+                mFocusedWin,
+                /* areVisible= */ false,
+                /* wereRevealedFromSwipeOnSystemBar= */ false);
+
         startAnimation(false /* show */, () -> {
             synchronized (mDisplayContent.mWmService.mGlobalLock) {
                 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
@@ -373,6 +384,11 @@
                     mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
         }
         mShowingTransientTypes.clear();
+
+        dispatchTransientSystemBarsVisibilityChanged(
+                mFocusedWin,
+                /* areVisible= */ false,
+                /* wereRevealedFromSwipeOnSystemBar= */ false);
     }
 
     private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
@@ -521,6 +537,32 @@
         listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
     }
 
+    private void dispatchTransientSystemBarsVisibilityChanged(
+            @Nullable WindowState focusedWindow,
+            boolean areVisible,
+            boolean wereRevealedFromSwipeOnSystemBar) {
+        if (focusedWindow == null) {
+            return;
+        }
+
+        Task task = focusedWindow.getTask();
+        if (task == null) {
+            return;
+        }
+
+        int taskId = task.mTaskId;
+        boolean isValidTaskId = taskId != ActivityTaskManager.INVALID_TASK_ID;
+        if (!isValidTaskId) {
+            return;
+        }
+
+        mDisplayContent.mWmService.mTaskSystemBarsListenerController
+                .dispatchTransientSystemBarVisibilityChanged(
+                        taskId,
+                        areVisible,
+                        wereRevealedFromSwipeOnSystemBar);
+    }
+
     private class BarWindow {
 
         private final int mId;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 76a7981..ee03d02 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -981,6 +981,29 @@
             mWmService.checkDrawnWindowsLocked();
         }
 
+        final int N = mWmService.mPendingRemove.size();
+        if (N > 0) {
+            if (mWmService.mPendingRemoveTmp.length < N) {
+                mWmService.mPendingRemoveTmp = new WindowState[N + 10];
+            }
+            mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
+            mWmService.mPendingRemove.clear();
+            ArrayList<DisplayContent> displayList = new ArrayList();
+            for (i = 0; i < N; i++) {
+                final WindowState w = mWmService.mPendingRemoveTmp[i];
+                w.removeImmediately();
+                final DisplayContent displayContent = w.getDisplayContent();
+                if (displayContent != null && !displayList.contains(displayContent)) {
+                    displayList.add(displayContent);
+                }
+            }
+
+            for (int j = displayList.size() - 1; j >= 0; --j) {
+                final DisplayContent dc = displayList.get(j);
+                dc.assignWindowLayers(true /*setLayoutNeeded*/);
+            }
+        }
+
         forAllDisplays(dc -> {
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             dc.updateSystemGestureExclusion();
diff --git a/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java b/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java
new file mode 100644
index 0000000..acb6061
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
+
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages dispatch of task system bar changes to interested listeners. All invocations must be
+ * performed while the {@link WindowManagerService#getWindowManagerLock() Window Manager Lock} is
+ * held.
+ */
+final class TaskSystemBarsListenerController {
+
+    private final HashSet<TaskSystemBarsListener> mListeners = new HashSet<>();
+    private final Executor mBackgroundExecutor;
+
+    TaskSystemBarsListenerController() {
+        this.mBackgroundExecutor = BackgroundThread.getExecutor();
+    }
+
+    void registerListener(TaskSystemBarsListener listener) {
+        mListeners.add(listener);
+    }
+
+    void unregisterListener(TaskSystemBarsListener listener) {
+        mListeners.remove(listener);
+    }
+
+    void dispatchTransientSystemBarVisibilityChanged(
+            int taskId,
+            boolean visible,
+            boolean wereRevealedFromSwipeOnSystemBar) {
+        HashSet<TaskSystemBarsListener> localListeners;
+        localListeners = new HashSet<>(mListeners);
+
+        mBackgroundExecutor.execute(() -> {
+            for (TaskSystemBarsListener listener : localListeners) {
+                listener.onTransientSystemBarsVisibilityChanged(
+                        taskId,
+                        visible,
+                        wereRevealedFromSwipeOnSystemBar);
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 20fa7a9..4900f929 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -256,6 +256,25 @@
     }
 
     /**
+     * An interface to be notified when the system bars for a task change.
+     */
+    public interface TaskSystemBarsListener {
+
+        /**
+         * Called when the visibility of the system bars of a task change.
+         *
+         * @param taskId the identifier of the task.
+         * @param visible if the transient system bars are visible.
+         * @param wereRevealedFromSwipeOnSystemBar if the transient bars were revealed due to a
+         *                                         swipe gesture on a system bar.
+         */
+        void onTransientSystemBarsVisibilityChanged(
+                int taskId,
+                boolean visible,
+                boolean wereRevealedFromSwipeOnSystemBar);
+    }
+
+    /**
      * An interface to be notified when keyguard exit animation should start.
      */
     public interface KeyguardExitAnimationStartListener {
@@ -519,6 +538,20 @@
     public abstract void registerAppTransitionListener(AppTransitionListener listener);
 
     /**
+     * Registers a listener to be notified to when the system bars of a task changes.
+     *
+     * @param listener The listener to register.
+     */
+    public abstract void registerTaskSystemBarsListener(TaskSystemBarsListener listener);
+
+    /**
+     * Registers a listener to be notified to when the system bars of a task changes.
+     *
+     * @param listener The listener to unregister.
+     */
+    public abstract void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener);
+
+    /**
      * Registers a listener to be notified to start the keyguard exit animation.
      *
      * @param listener The listener to register.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b37cb4f..1167cb5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -222,6 +222,7 @@
 import android.util.EventLog;
 import android.util.MergedConfiguration;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
@@ -586,6 +587,20 @@
     final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
 
     /**
+     * Windows whose animations have ended and now must be removed.
+     */
+    final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
+
+    /**
+     * Used when processing mPendingRemove to avoid working on the original array.
+     */
+    WindowState[] mPendingRemoveTmp = new WindowState[20];
+
+    // TODO: use WindowProcessController once go/wm-unified is done.
+    /** Mapping of process pids to configurations */
+    final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>();
+
+    /**
      * Mapping of displayId to {@link DisplayImePolicy}.
      * Note that this can be accessed without holding the lock.
      */
@@ -683,6 +698,7 @@
             () -> mDisplayRotationController = null;
 
     final DisplayWindowListenerController mDisplayNotificationController;
+    final TaskSystemBarsListenerController mTaskSystemBarsListenerController;
 
     boolean mDisplayFrozen = false;
     long mDisplayFreezeTime = 0;
@@ -1265,6 +1281,7 @@
         mScreenFrozenLock.setReferenceCounted(false);
 
         mDisplayNotificationController = new DisplayWindowListenerController(this);
+        mTaskSystemBarsListenerController = new TaskSystemBarsListenerController();
 
         mActivityManager = ActivityManager.getService();
         mActivityTaskManager = ActivityTaskManager.getService();
@@ -2036,6 +2053,7 @@
             dc.mWinRemovedSinceNullFocus.add(win);
         }
         mEmbeddedWindowController.onWindowRemoved(win);
+        mPendingRemove.remove(win);
         mResizingWindows.remove(win);
         updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
         mWindowsChanged = true;
@@ -6364,6 +6382,23 @@
                 }
             }
         }
+        if (mPendingRemove.size() > 0) {
+            pw.println();
+            pw.println("  Remove pending for:");
+            for (int i=mPendingRemove.size()-1; i>=0; i--) {
+                WindowState w = mPendingRemove.get(i);
+                if (windows == null || windows.contains(w)) {
+                    pw.print("  Remove #"); pw.print(i); pw.print(' ');
+                            pw.print(w);
+                    if (dumpAll) {
+                        pw.println(":");
+                        w.dump(pw, "    ", true);
+                    } else {
+                        pw.println();
+                    }
+                }
+            }
+        }
         if (mForceRemoves != null && mForceRemoves.size() > 0) {
             pw.println();
             pw.println("  Windows force removing:");
@@ -7591,6 +7626,20 @@
         }
 
         @Override
+        public void registerTaskSystemBarsListener(TaskSystemBarsListener listener) {
+            synchronized (mGlobalLock) {
+                mTaskSystemBarsListenerController.registerListener(listener);
+            }
+        }
+
+        @Override
+        public void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener) {
+            synchronized (mGlobalLock) {
+                mTaskSystemBarsListenerController.unregisterListener(listener);
+            }
+        }
+
+        @Override
         public void registerKeyguardExitAnimationStartListener(
                 KeyguardExitAnimationStartListener listener) {
             synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0a02b44..79c64b1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4887,20 +4887,15 @@
             if (hasSurface) {
                 mWmService.mDestroySurface.add(this);
             }
+            if (mRemoveOnExit) {
+                mWmService.mPendingRemove.add(this);
+                mRemoveOnExit = false;
+            }
         }
         mAnimatingExit = false;
         getDisplayContent().mWallpaperController.hideWallpapers(this);
     }
 
-    @Override
-    boolean handleCompleteDeferredRemoval() {
-        if (mRemoveOnExit) {
-            mRemoveOnExit = false;
-            removeImmediately();
-        }
-        return super.handleCompleteDeferredRemoval();
-    }
-
     boolean clearAnimatingFlags() {
         boolean didSomething = false;
         // We don't want to clear it out for windows that get replaced, because the
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 161d7ce..166a0f5 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -244,6 +244,9 @@
     return hasLatLong(location.v1_0);
 }
 
+bool isSvStatusRegistered = false;
+bool isNmeaRegistered = false;
+
 }  // namespace
 
 static inline jboolean boolToJbool(bool value) {
@@ -505,6 +508,13 @@
 
 template <class T_list, class T_sv_info>
 Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) {
+    // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework.
+    if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() == 1) {
+        if (!isSvStatusRegistered) {
+            return Void();
+        }
+    }
+
     JNIEnv* env = getJniEnv();
 
     uint32_t listSize = getGnssSvInfoListSize(svStatus);
@@ -566,8 +576,12 @@
     return Void();
 }
 
-Return<void> GnssCallback::gnssNmeaCb(
-    int64_t timestamp, const ::android::hardware::hidl_string& nmea) {
+Return<void> GnssCallback::gnssNmeaCb(int64_t timestamp,
+                                      const ::android::hardware::hidl_string& nmea) {
+    // In HIDL, if no listener is registered, do not report nmea to the framework.
+    if (!isNmeaRegistered) {
+        return Void();
+    }
     JNIEnv* env = getJniEnv();
     /*
      * The Java code will call back to read these values.
@@ -680,6 +694,12 @@
 }
 
 Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
+    // In AIDL v1, if no listener is registered, do not report nmea to the framework.
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() == 1) {
+        if (!isNmeaRegistered) {
+            return Status::ok();
+        }
+    }
     JNIEnv* env = getJniEnv();
     /*
      * The Java code will call back to read these values.
@@ -1562,6 +1582,58 @@
     return checkHidlReturn(result, "IGnss stop() failed.");
 }
 
+static jboolean android_location_gnss_hal_GnssNative_start_sv_status_collection(JNIEnv* /* env */,
+                                                                                jclass) {
+    isSvStatusRegistered = true;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->startSvStatus();
+        return checkAidlStatus(status, "IGnssAidl startSvStatus() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static jboolean android_location_gnss_hal_GnssNative_stop_sv_status_collection(JNIEnv* /* env */,
+                                                                               jclass) {
+    isSvStatusRegistered = false;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->stopSvStatus();
+        return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static jboolean android_location_gnss_hal_GnssNative_start_nmea_message_collection(
+        JNIEnv* /* env */, jclass) {
+    isNmeaRegistered = true;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->startNmea();
+        return checkAidlStatus(status, "IGnssAidl startNmea() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static jboolean android_location_gnss_hal_GnssNative_stop_nmea_message_collection(JNIEnv* /* env */,
+                                                                                  jclass) {
+    isNmeaRegistered = false;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->stopNmea();
+        return checkAidlStatus(status, "IGnssAidl stopNmea() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
 static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
                                                                     jint flags) {
     if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
@@ -1989,7 +2061,7 @@
     jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
     jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc);
     uint16_t corrFlags = static_cast<uint16_t>(correctionFlags);
-    jobject reflectingPlaneObj;
+    jobject reflectingPlaneObj = nullptr;
     bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0;
     if (has_ref_plane) {
         reflectingPlaneObj =
@@ -2013,6 +2085,7 @@
                 .azimuthDegrees = azimuthDegreeRefPlane,
         };
     }
+    env->DeleteLocalRef(reflectingPlaneObj);
 
     SingleSatCorrection_V1_0 singleSatCorrection = {
             .singleSatCorrectionFlags = corrFlags,
@@ -2044,6 +2117,7 @@
         };
 
         list[i] = singleSatCorrection_1_1;
+        env->DeleteLocalRef(singleSatCorrectionObj);
     }
 }
 
@@ -2061,6 +2135,7 @@
         singleSatCorrection.constellation = static_cast<GnssConstellationType_V1_0>(constType),
 
         list[i] = singleSatCorrection;
+        env->DeleteLocalRef(singleSatCorrectionObj);
     }
 }
 
@@ -2131,6 +2206,7 @@
 
     hidl_vec<SingleSatCorrection_V1_0> list(len);
     getSingleSatCorrectionList_1_0(env, singleSatCorrectionList, list);
+    env->DeleteLocalRef(singleSatCorrectionList);
     measurementCorrections_1_0.satCorrections = list;
 
     auto result = gnssCorrectionsIface_V1_0->setCorrections(measurementCorrections_1_0);
@@ -2365,6 +2441,16 @@
         {"native_is_gnss_visibility_control_supported", "()Z",
          reinterpret_cast<void*>(
                  android_location_gnss_hal_GnssNative_is_gnss_visibility_control_supported)},
+        {"native_start_sv_status_collection", "()Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start_sv_status_collection)},
+        {"native_stop_sv_status_collection", "()Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop_sv_status_collection)},
+        {"native_start_nmea_message_collection", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_start_nmea_message_collection)},
+        {"native_stop_nmea_message_collection", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_stop_nmea_message_collection)},
 };
 
 static const JNINativeMethod sBatchingMethods[] = {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 26c442d..e18e002 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -23,6 +23,7 @@
 import android.content.ComponentName;
 import android.os.FileUtils;
 import android.os.PersistableBundle;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -83,7 +84,7 @@
     private static final String ATTR_NEW_USER_DISCLAIMER = "new-user-disclaimer";
 
     // Values of ATTR_NEW_USER_DISCLAIMER
-    static final String NEW_USER_DISCLAIMER_SHOWN = "shown";
+    static final String NEW_USER_DISCLAIMER_ACKNOWLEDGED = "acked";
     static final String NEW_USER_DISCLAIMER_NOT_NEEDED = "not_needed";
     static final String NEW_USER_DISCLAIMER_NEEDED = "needed";
 
@@ -613,6 +614,28 @@
         }
     }
 
+    boolean isNewUserDisclaimerAcknowledged() {
+        if (mNewUserDisclaimer == null) {
+            if (mUserId == UserHandle.USER_SYSTEM) {
+                return true;
+            }
+            Slogf.w(TAG, "isNewUserDisclaimerAcknowledged(%d): mNewUserDisclaimer is null",
+                    mUserId);
+            return false;
+        }
+        switch (mNewUserDisclaimer) {
+            case NEW_USER_DISCLAIMER_ACKNOWLEDGED:
+            case NEW_USER_DISCLAIMER_NOT_NEEDED:
+                return true;
+            case NEW_USER_DISCLAIMER_NEEDED:
+                return false;
+            default:
+                Slogf.w(TAG, "isNewUserDisclaimerAcknowledged(%d): invalid value %d", mUserId,
+                        mNewUserDisclaimer);
+                return false;
+        }
+    }
+
     void dump(IndentingPrintWriter pw) {
         pw.println();
         pw.println("Enabled Device Admins (User " + mUserId + ", provisioningState: "
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e34178a..6f41d42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10978,10 +10978,11 @@
     @Override
     public void acknowledgeNewUserDisclaimer() {
         CallerIdentity callerIdentity = getCallerIdentity();
-        canManageUsers(callerIdentity);
+        Preconditions.checkCallAuthorization(canManageUsers(callerIdentity)
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
 
         setShowNewUserDisclaimer(callerIdentity.getUserId(),
-                DevicePolicyData.NEW_USER_DISCLAIMER_SHOWN);
+                DevicePolicyData.NEW_USER_DISCLAIMER_ACKNOWLEDGED);
     }
 
     private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) {
@@ -11014,6 +11015,18 @@
     }
 
     @Override
+    public boolean isNewUserDisclaimerAcknowledged() {
+        CallerIdentity callerIdentity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(canManageUsers(callerIdentity)
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
+        int userId = callerIdentity.getUserId();
+        synchronized (getLockObject()) {
+            DevicePolicyData policyData = getUserData(userId);
+            return policyData.isNewUserDisclaimerAcknowledged();
+        }
+    }
+
+    @Override
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
         Objects.requireNonNull(who, "ComponentName is null");
         Objects.requireNonNull(userHandle, "UserHandle is null");
@@ -11210,8 +11223,8 @@
     @Override
     public int logoutUserInternal() {
         CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                canManageUsers(caller) || hasCallingOrSelfPermission(permission.CREATE_USERS));
+        Preconditions.checkCallAuthorization(canManageUsers(caller)
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
 
         int result = logoutUserUnchecked(getCurrentForegroundUserId());
         Slogf.d(LOG_TAG, "logout called by uid %d. Result: %d", caller.getUid(), result);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c9aeabd..d0c861f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -366,6 +366,8 @@
             "com.android.server.adb.AdbService$Lifecycle";
     private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
             "com.android.server.speech.SpeechRecognitionManagerService";
+    private static final String WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS =
+            "com.android.server.wallpapereffectsgeneration.WallpaperEffectsGenerationManagerService";
     private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
             "com.android.server.appprediction.AppPredictionManagerService";
     private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -1895,7 +1897,6 @@
             }
             t.traceEnd();
 
-
             t.traceBegin("StartIpSecService");
             try {
                 ipSecService = IpSecService.create(context);
@@ -2126,6 +2127,14 @@
                 Slog.i(TAG, "Wallpaper service disabled by config");
             }
 
+            // WallpaperEffectsGeneration manager service
+            // TODO (b/135218095): Use deviceHasConfigString(context,
+            //  R.string.config_defaultWallpaperEffectsGenerationService)
+            t.traceBegin("StartWallpaperEffectsGenerationService");
+            mSystemServiceManager.startService(
+                    WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS);
+            t.traceEnd();
+
             t.traceBegin("StartAudioService");
             if (!isArc) {
                 mSystemServiceManager.startService(AudioService.Lifecycle.class);
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 9e221be..75669d5 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -52,6 +52,7 @@
         "service-blobstore",
         "service-jobscheduler",
         "service-permission.impl",
+        "service-supplementalprocess.impl",
         "services.core",
         "services.devicepolicy",
         "services.net",
diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml
new file mode 100644
index 0000000..eb15451
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<game-mode-config
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:supportsPerformanceGameMode="false"
+    android:supportsBatteryGameMode="false"
+    android:allowGameAngleDriver="false"
+    android:allowGameDownscaling="false"
+    android:allowGameFpsOverride="false"
+/>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml
new file mode 100644
index 0000000..65b7467
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<game-mode-config
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:supportsPerformanceGameMode="false"
+    android:supportsBatteryGameMode="false"
+    android:allowGameAngleDriver="true"
+    android:allowGameDownscaling="true"
+    android:allowGameFpsOverride="true"
+/>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
index fec9b12..e89c812 100644
--- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
@@ -359,6 +359,34 @@
                 LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
     }
 
+    @Test
+    public void dispatchTransientVisibilityChanged_valueUnchanged_doesNotInvokeCallback() {
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+
+        assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures).hasSize(0);
+    }
+
+    @Test
+    public void dispatchTransientVisibilityChanged_valueChanged_invokesCallback() {
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+
+        assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures)
+                .containsExactly(true).inOrder();
+    }
+
+    @Test
+    public void dispatchTransientVisibilityChanged_manyTimes_invokesCallbackWhenValueChanges() {
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+        mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+
+        assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures)
+                .containsExactly(true, false, true).inOrder();
+    }
+
     private static class LifecycleTrackingGameSession extends GameSession {
         private enum LifecycleMethodCall {
             ON_CREATE,
@@ -368,6 +396,8 @@
         }
 
         final List<LifecycleMethodCall> mLifecycleMethodCalls = new ArrayList<>();
+        final List<Boolean> mCapturedTransientSystemBarVisibilityFromRevealGestures =
+                new ArrayList<>();
 
         @Override
         public void onCreate() {
@@ -387,5 +417,11 @@
                 mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED);
             }
         }
+
+        @Override
+        public void onTransientSystemBarVisibilityFromRevealGestureChanged(
+                boolean visibleDueToGesture) {
+            mCapturedTransientSystemBarVisibilityFromRevealGestures.add(visibleDueToGesture);
+        }
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 28c91aa..7670953 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -101,6 +101,7 @@
 import android.os.UidBatteryConsumer;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.Pair;
 
@@ -211,6 +212,7 @@
     @Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal;
     @Mock private MediaSessionManager mMediaSessionManager;
     @Mock private RoleManager mRoleManager;
+    @Mock private TelephonyManager mTelephonyManager;
 
     private long mCurrentTimeMillis;
 
@@ -2309,6 +2311,11 @@
         }
 
         @Override
+        TelephonyManager getTelephonyManager() {
+            return mTelephonyManager;
+        }
+
+        @Override
         AppFGSTracker getAppFGSTracker() {
             return mAppFGSTracker;
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 44b81d4..d2358a0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -26,6 +26,8 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -41,6 +43,9 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
 import android.hardware.power.Mode;
 import android.os.Bundle;
 import android.os.PowerManagerInternal;
@@ -159,11 +164,17 @@
         mPackageName = mMockContext.getPackageName();
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
+        applicationInfo.packageName = mPackageName;
         final PackageInfo pi = new PackageInfo();
         pi.packageName = mPackageName;
         pi.applicationInfo = applicationInfo;
         final List<PackageInfo> packages = new ArrayList<>();
         packages.add(pi);
+
+        final Resources resources =
+                InstrumentationRegistry.getInstrumentation().getContext().getResources();
+        when(mMockPackageManager.getResourcesForApplication(anyString()))
+                .thenReturn(resources);
         when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
                 .thenReturn(packages);
         when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
@@ -322,6 +333,46 @@
                 .thenReturn(applicationInfo);
     }
 
+    private void mockInterventionsEnabledFromXml() throws Exception {
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+        Bundle metaDataBundle = new Bundle();
+        final int resId = 123;
+        metaDataBundle.putInt(
+                GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
+        applicationInfo.metaData = metaDataBundle;
+        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
+        seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
+                "res/xml/gama_manager_service_metadata_config_enabled.xml");
+    }
+
+    private void mockInterventionsDisabledFromXml() throws Exception {
+        final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+                mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+        Bundle metaDataBundle = new Bundle();
+        final int resId = 123;
+        metaDataBundle.putInt(
+                GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
+        applicationInfo.metaData = metaDataBundle;
+        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
+        seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
+                "res/xml/gama_manager_service_metadata_config_disabled.xml");
+    }
+
+
+    private void seedGameManagerServiceMetaDataFromFile(String packageName, int resId,
+            String fileName)
+            throws Exception {
+        AssetManager assetManager =
+                InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+        XmlResourceParser xmlResourceParser =
+                assetManager.openXmlResourceParser(fileName);
+        when(mMockPackageManager.getXml(eq(packageName), eq(resId), any()))
+                .thenReturn(xmlResourceParser);
+    }
+
     /**
      * By default game mode is not supported.
      */
@@ -511,8 +562,8 @@
                 gameManagerService.getConfig(mPackageName);
         assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
 
-        // Validate GameManagerService.getAngleEnabled() returns the correct value.
-        assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
+        // Validate GameManagerService.isAngleEnabled() returns the correct value.
+        assertEquals(gameManagerService.isAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
     }
 
     private void checkFps(GameManagerService gameManagerService, int gameMode, int fps) {
@@ -523,7 +574,7 @@
         }
         GameManagerService.GamePackageConfiguration config =
                 gameManagerService.getConfig(mPackageName);
-        assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps);
+        assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
     }
 
     /**
@@ -905,6 +956,36 @@
     }
 
     @Test
+    public void testGameModeConfigAllowFpsTrue() throws Exception {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        mockInterventionsEnabledFromXml();
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        GameManagerService.GamePackageConfiguration config =
+                gameManagerService.getConfig(mPackageName);
+        assertEquals(90,
+                config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
+        assertEquals(30, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
+    }
+
+    @Test
+    public void testGameModeConfigAllowFpsFalse() throws Exception {
+        mockDeviceConfigAll();
+        mockModifyGameModeGranted();
+        mockInterventionsDisabledFromXml();
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
+        startUser(gameManagerService, USER_ID_1);
+        GameManagerService.GamePackageConfiguration config =
+                gameManagerService.getConfig(mPackageName);
+        assertEquals(0,
+                config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
+        assertEquals(0, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
+    }
+
+    @Test
     public void testInterventionFps() throws Exception {
         mockDeviceConfigAll();
         mockModifyGameModeGranted();
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index 317a51b..08de62b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -31,7 +31,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.Manifest;
@@ -72,6 +71,7 @@
 import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 import com.android.internal.util.Preconditions;
 import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
 import com.android.server.wm.WindowManagerService;
 
 import org.junit.After;
@@ -112,6 +112,7 @@
     private static final ComponentName GAME_B_MAIN_ACTIVITY =
             new ComponentName(GAME_B_PACKAGE, "com.package.game.b.MainActivity");
 
+
     private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
 
     private MockitoSession mMockingSession;
@@ -131,6 +132,7 @@
     private FakeGameSessionService mFakeGameSessionService;
     private FakeServiceConnector<IGameSessionService> mFakeGameSessionServiceConnector;
     private ArrayList<ITaskStackListener> mTaskStackListeners;
+    private ArrayList<TaskSystemBarsListener> mTaskSystemBarsListeners;
     private ArrayList<RunningTaskInfo> mRunningTaskInfos;
 
     @Mock
@@ -159,15 +161,25 @@
             mTaskStackListeners.add(invocation.getArgument(0));
             return null;
         }).when(mMockActivityTaskManager).registerTaskStackListener(any());
+        doAnswer(invocation -> {
+            mTaskStackListeners.remove(invocation.getArgument(0));
+            return null;
+        }).when(mMockActivityTaskManager).unregisterTaskStackListener(any());
+
+        mTaskSystemBarsListeners = new ArrayList<>();
+        doAnswer(invocation -> {
+            mTaskSystemBarsListeners.add(invocation.getArgument(0));
+            return null;
+        }).when(mMockWindowManagerInternal).registerTaskSystemBarsListener(any());
+        doAnswer(invocation -> {
+            mTaskSystemBarsListeners.remove(invocation.getArgument(0));
+            return null;
+        }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
 
         mRunningTaskInfos = new ArrayList<>();
         when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
                 mRunningTaskInfos);
 
-        doAnswer(invocation -> {
-            mTaskStackListeners.remove(invocation.getArgument(0));
-            return null;
-        }).when(mMockActivityTaskManager).unregisterTaskStackListener(any());
 
         mGameServiceProviderInstance = new GameServiceProviderInstanceImpl(
                 new UserHandle(USER_ID),
@@ -394,7 +406,60 @@
                 .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
 
         verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
-        verifyNoMoreInteractions(mMockWindowManagerInternal);
+    }
+
+    @Test
+    public void taskSystemBarsListenerChanged_noAssociatedGameSession_doesNothing() {
+        mGameServiceProviderInstance.start();
+
+        dispatchTaskSystemBarsEvent(taskSystemBarsListener -> {
+            taskSystemBarsListener.onTransientSystemBarsVisibilityChanged(
+                    10,
+                    /* areVisible= */ false,
+                    /* wereRevealedFromSwipeOnSystemBar= */ false);
+        });
+    }
+
+    @Test
+    public void systemBarsTransientShownDueToGesture_hasGameSession_propagatesToGameSession() {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        dispatchTaskSystemBarsEvent(taskSystemBarsListener -> {
+            taskSystemBarsListener.onTransientSystemBarsVisibilityChanged(
+                    10,
+                    /* areVisible= */ true,
+                    /* wereRevealedFromSwipeOnSystemBar= */ true);
+        });
+
+        assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isTrue();
+    }
+
+    @Test
+    public void systemBarsTransientShownButNotGesture_hasGameSession_notPropagatedToGameSession() {
+        mGameServiceProviderInstance.start();
+        startTask(10, GAME_A_MAIN_ACTIVITY);
+        mFakeGameService.requestCreateGameSession(10);
+
+        FakeGameSession gameSession10 = new FakeGameSession();
+        SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+        mFakeGameSessionService.removePendingFutureForTaskId(10)
+                .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+        dispatchTaskSystemBarsEvent(taskSystemBarsListener -> {
+            taskSystemBarsListener.onTransientSystemBarsVisibilityChanged(
+                    10,
+                    /* areVisible= */ true,
+                    /* wereRevealedFromSwipeOnSystemBar= */ false);
+        });
+
+        assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isFalse();
     }
 
     @Test
@@ -492,7 +557,6 @@
 
         verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
         verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10));
-        verifyNoMoreInteractions(mMockWindowManagerInternal);
     }
 
     @Test
@@ -830,6 +894,13 @@
         mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED);
     }
 
+    private void dispatchTaskSystemBarsEvent(
+            ThrowingConsumer<TaskSystemBarsListener> taskSystemBarsListenerConsumer) {
+        for (TaskSystemBarsListener listener : mTaskSystemBarsListeners) {
+            taskSystemBarsListenerConsumer.accept(listener);
+        }
+    }
+
     static final class FakeGameService extends IGameService.Stub {
         private IGameServiceController mGameServiceController;
 
@@ -944,6 +1015,7 @@
     private static class FakeGameSession extends IGameSession.Stub {
         boolean mIsDestroyed = false;
         boolean mIsFocused = false;
+        boolean mAreTransientSystemBarsVisibleFromRevealGesture = false;
 
         @Override
         public void onDestroyed() {
@@ -954,6 +1026,11 @@
         public void onTaskFocusChanged(boolean focused) {
             mIsFocused = focused;
         }
+
+        @Override
+        public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean areVisible) {
+            mAreTransientSystemBarsVisibleFromRevealGesture = areVisible;
+        }
     }
 
     private final class MockContext extends ContextWrapper {
@@ -1005,5 +1082,4 @@
             return mLastStartedIntent;
         }
     }
-
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index bdeb2b4..f9bdad6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -204,6 +204,49 @@
                 jobInfoBuilder.build(), callingUid, "com.android.test", 0, testTag);
     }
 
+    @Test
+    public void testGetMinJobExecutionGuaranteeMs() {
+        JobStatus ejMax = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+                createJobInfo(1).setExpedited(true));
+        JobStatus ejHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+                createJobInfo(2).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH));
+        JobStatus ejMaxDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+                createJobInfo(3).setExpedited(true));
+        JobStatus ejHighDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+                createJobInfo(4).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH));
+        JobStatus jobHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+                createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH));
+        JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+                createJobInfo(6));
+
+        spyOn(ejMax);
+        spyOn(ejHigh);
+        spyOn(ejMaxDowngraded);
+        spyOn(ejHighDowngraded);
+        spyOn(jobHigh);
+        spyOn(jobDef);
+
+        when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true);
+        when(ejHigh.shouldTreatAsExpeditedJob()).thenReturn(true);
+        when(ejMaxDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false);
+        when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false);
+        when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false);
+        when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false);
+
+        assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS,
+                mService.getMinJobExecutionGuaranteeMs(ejMax));
+        assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS,
+                mService.getMinJobExecutionGuaranteeMs(ejHigh));
+        assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+                mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded));
+        assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+                mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded));
+        assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+                mService.getMinJobExecutionGuaranteeMs(jobHigh));
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
+                mService.getMinJobExecutionGuaranteeMs(jobDef));
+    }
+
     /**
      * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
      * with the correct delay and deadline constraints if the periodic job is scheduled with the
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 153ce17..9d6793c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -30,6 +30,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RARE_INDEX;
@@ -536,6 +537,7 @@
 
         ExecutionStats expectedStats = new ExecutionStats();
         expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
         expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
@@ -595,6 +597,7 @@
         assertNotNull(mQuotaController.getEJTimingSessions(10, "com.android.test"));
 
         ExecutionStats expectedStats = new ExecutionStats();
+        expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
         expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
@@ -638,11 +641,13 @@
         ExecutionStats expectedStats = new ExecutionStats();
         ExecutionStats inputStats = new ExecutionStats();
 
+        inputStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
         inputStats.jobCountLimit = expectedStats.jobCountLimit = 100;
         inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
         // Invalid time is now +24 hours since there are no sessions at all for the app.
         expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
         synchronized (mQuotaController.mLock) {
             mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
         }
@@ -827,6 +832,8 @@
 
         ExecutionStats expectedStats = new ExecutionStats();
         ExecutionStats inputStats = new ExecutionStats();
+        inputStats.allowedTimePerPeriodMs = expectedStats.allowedTimePerPeriodMs =
+                10 * MINUTE_IN_MILLIS;
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
         inputStats.jobCountLimit = expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
         inputStats.sessionCountLimit = expectedStats.sessionCountLimit =
@@ -924,6 +931,7 @@
         ExecutionStats expectedStats = new ExecutionStats();
 
         // Active
+        expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
         expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
@@ -1006,6 +1014,7 @@
         ExecutionStats expectedStats = new ExecutionStats();
 
         // Active
+        expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
         expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
@@ -1242,6 +1251,7 @@
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 6 * HOUR_IN_MILLIS);
 
         ExecutionStats expectedStats = new ExecutionStats();
+        expectedStats.allowedTimePerPeriodMs = originalStatsActive.allowedTimePerPeriodMs;
         expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
         expectedStats.jobCountLimit = originalStatsActive.jobCountLimit;
         expectedStats.sessionCountLimit = originalStatsActive.sessionCountLimit;
@@ -1261,6 +1271,7 @@
         assertTrue(originalStatsActive == newStatsActive);
         assertEquals(expectedStats, newStatsActive);
 
+        expectedStats.allowedTimePerPeriodMs = originalStatsWorking.allowedTimePerPeriodMs;
         expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
         expectedStats.jobCountLimit = originalStatsWorking.jobCountLimit;
         expectedStats.sessionCountLimit = originalStatsWorking.sessionCountLimit;
@@ -1277,6 +1288,7 @@
         assertTrue(originalStatsWorking == newStatsWorking);
         assertNotEquals(expectedStats, newStatsWorking);
 
+        expectedStats.allowedTimePerPeriodMs = originalStatsFrequent.allowedTimePerPeriodMs;
         expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
         expectedStats.jobCountLimit = originalStatsFrequent.jobCountLimit;
         expectedStats.sessionCountLimit = originalStatsFrequent.sessionCountLimit;
@@ -1293,6 +1305,7 @@
         assertTrue(originalStatsFrequent == newStatsFrequent);
         assertNotEquals(expectedStats, newStatsFrequent);
 
+        expectedStats.allowedTimePerPeriodMs = originalStatsRare.allowedTimePerPeriodMs;
         expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
         expectedStats.jobCountLimit = originalStatsRare.jobCountLimit;
         expectedStats.sessionCountLimit = originalStatsRare.sessionCountLimit;
@@ -1354,7 +1367,8 @@
     @Test
     public void testGetMaxJobExecutionTimeLocked_Regular_Active() {
         JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+                10 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 10 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 2 * HOUR_IN_MILLIS);
         setDischarging();
@@ -2886,11 +2900,12 @@
     public void testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow() {
         // Set rate limiting period different from allowed time to confirm code sets based on
         // the former.
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+        final int standbyBucket = WORKING_INDEX;
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                10 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 5 * MINUTE_IN_MILLIS);
 
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        final int standbyBucket = WORKING_INDEX;
 
         JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked", 1);
         setStandbyBucket(standbyBucket, jobStatus);
@@ -2953,8 +2968,8 @@
     @Test
     public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() {
         // Make sure any new value is used correctly.
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
-                mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS / 2);
 
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
         mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -2977,8 +2992,8 @@
         // Make sure any new value is used correctly.
         setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS,
                 mQcConstants.IN_QUOTA_BUFFER_MS * 2);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
-                mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS / 2);
         setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS,
                 mQcConstants.MAX_EXECUTION_TIME_MS / 2);
 
@@ -3002,7 +3017,8 @@
         // Working set window size is 2 hours.
         final int standbyBucket = WORKING_INDEX;
         final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
-        final long remainingTimeMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_MS - contributionMs;
+        final long remainingTimeMs =
+                mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS - contributionMs;
 
         // Session straddles edge of bucket window. Only the contribution should be counted towards
         // the quota.
@@ -3062,16 +3078,28 @@
 
     @Test
     public void testConstantsUpdating_ValidValues() {
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 5 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                8 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+                5 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                7 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+                2 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 4 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                11 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
         setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, .7f);
         setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .2f);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 45 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 60 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 120 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 3 * HOUR_IN_MILLIS);
+        setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_EXEMPTED, 6000);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, 5000);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 4000);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 3000);
@@ -3079,6 +3107,7 @@
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, 2000);
         setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * MINUTE_IN_MILLIS);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 500);
+        setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_EXEMPTED, 600);
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 500);
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 400);
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 300);
@@ -3088,6 +3117,7 @@
         setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
                 10 * SECOND_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, 3 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 2 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 90 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS);
@@ -3104,10 +3134,23 @@
                 84 * SECOND_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS);
 
-        assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+        assertEquals(8 * MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
+        assertEquals(5 * MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]);
+        assertEquals(7 * MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]);
+        assertEquals(2 * MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
+        assertEquals(4 * MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]);
+        assertEquals(11 * MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
         assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
         assertEquals(.7f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
         assertEquals(.2f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
+        assertEquals(99 * MINUTE_IN_MILLIS,
+                mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
         assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
         assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
         assertEquals(45 * MINUTE_IN_MILLIS,
@@ -3118,12 +3161,14 @@
         assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
         assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
         assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow());
+        assertEquals(6000, mQuotaController.getBucketMaxJobCounts()[EXEMPTED_INDEX]);
         assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
         assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
         assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
         assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
         assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
         assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
+        assertEquals(600, mQuotaController.getBucketMaxSessionCounts()[EXEMPTED_INDEX]);
         assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
         assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
         assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
@@ -3132,6 +3177,7 @@
         assertEquals(10 * SECOND_IN_MILLIS,
                 mQuotaController.getTimingSessionCoalescingDurationMs());
         assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
+        assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]);
         assertEquals(2 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]);
         assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
@@ -3151,16 +3197,24 @@
     @Test
     public void testConstantsUpdating_InvalidValues() {
         // Test negatives/too low.
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, -.1f);
         setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, -.01f);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, -MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, -MINUTE_IN_MILLIS);
+        setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_EXEMPTED, -1);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, -1);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 1);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 1);
@@ -3168,6 +3222,7 @@
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, -1);
         setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * SECOND_IN_MILLIS);
         setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 0);
+        setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_EXEMPTED, -1);
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, -1);
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 0);
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, -3);
@@ -3176,6 +3231,7 @@
         setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0);
         setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, -1);
@@ -3191,10 +3247,19 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1);
 
-        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+        assertEquals(MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
         assertEquals(0, mQuotaController.getInQuotaBufferMs());
         assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
         assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
@@ -3203,12 +3268,14 @@
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
         assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
         assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow());
+        assertEquals(10, mQuotaController.getBucketMaxJobCounts()[EXEMPTED_INDEX]);
         assertEquals(10, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
         assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
         assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
         assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
         assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
         assertEquals(10, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
+        assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[EXEMPTED_INDEX]);
         assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
         assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
         assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
@@ -3216,6 +3283,7 @@
         assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
         assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
         assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs());
+        assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]);
         assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]);
         assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
         assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
@@ -3233,17 +3301,37 @@
 
         // Invalid configurations.
         // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 2 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+                10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+                2 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                10 * MINUTE_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 5 * MINUTE_IN_MILLIS);
 
         assertTrue(mQuotaController.getInQuotaBufferMs()
-                <= mQuotaController.getAllowedTimePerPeriodMs());
+                <= mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
 
         // Test larger than a day. Controller should cap at one day.
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+                25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+                25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, 1f);
         setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .95f);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
@@ -3254,6 +3342,7 @@
         setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
                 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
@@ -3269,10 +3358,21 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
 
-        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+        assertEquals(24 * HOUR_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS,
+                mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
         assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
         assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
         assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
@@ -3284,6 +3384,7 @@
         assertEquals(15 * MINUTE_IN_MILLIS,
                 mQuotaController.getTimingSessionCoalescingDurationMs());
         assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 6ae0031..4ec1641 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -69,6 +69,7 @@
 import com.android.server.pm.pkg.parsing.ParsingPackage
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.supplementalprocess.SupplementalProcessManagerLocal
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
 import com.android.server.testutils.nullable
@@ -335,6 +336,7 @@
         stageServicesExtensionScan()
         stageSystemSharedLibraryScan()
         stagePermissionsControllerScan()
+        stageSupplementalProcessScan()
         stageInstantAppResolverScan()
     }
 
@@ -569,6 +571,22 @@
     }
 
     @Throws(Exception::class)
+    private fun stageSupplementalProcessScan() {
+        stageScanNewPackage("com.android.supplemental.process",
+                1L, systemPartitions[0].privAppFolder,
+                withPackage = { pkg: PackageImpl ->
+                    val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg)
+                    mockQueryServices(SupplementalProcessManagerLocal.SERVICE_INTERFACE,
+                            createBasicServiceInfo(
+                                    pkg, applicationInfo, "SupplementalProcessService"))
+                    pkg
+                },
+                withSetting = { setting: PackageSettingBuilder ->
+                    setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+                })
+    }
+
+    @Throws(Exception::class)
     private fun stageSystemSharedLibraryScan() {
         stageScanNewPackage("android.ext.shared",
                 1L, systemPartitions[0].appFolder,
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 9ee1205..3890d4d 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -24,14 +24,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.fail;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
@@ -42,6 +48,7 @@
 import android.provider.DeviceConfig;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
+import android.service.attention.IProximityCallback;
 
 import androidx.test.filters.SmallTest;
 
@@ -49,6 +56,7 @@
 import com.android.server.attention.AttentionManagerService.AttentionCheckCache;
 import com.android.server.attention.AttentionManagerService.AttentionCheckCacheBuffer;
 import com.android.server.attention.AttentionManagerService.AttentionHandler;
+import com.android.server.attention.AttentionManagerService.ProximityUpdate;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -59,10 +67,13 @@
 /**
  * Tests for {@link com.android.server.attention.AttentionManagerService}
  */
+@SuppressWarnings("GuardedBy")
 @SmallTest
 public class AttentionManagerServiceTest {
+    private static final double PROXIMITY_SUCCESS_STATE = 1.0;
     private AttentionManagerService mSpyAttentionManager;
     private final int mTimeout = 1000;
+    private final Object mLock = new Object();
     @Mock
     private AttentionCallbackInternal mMockAttentionCallbackInternal;
     @Mock
@@ -73,6 +84,8 @@
     private IThermalService mMockIThermalService;
     @Mock
     Context mContext;
+    @Mock
+    private ProximityCallbackInternal mMockProximityCallbackInternal;
 
     @Before
     public void setUp() throws RemoteException {
@@ -84,7 +97,6 @@
         doReturn(true).when(mMockIPowerManager).isInteractive();
         mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
 
-        Object mLock = new Object();
         // setup a spy on attention manager
         AttentionManagerService attentionManager = new AttentionManagerService(
                 mContext,
@@ -100,6 +112,119 @@
                 mSpyAttentionManager);
         mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck;
         mSpyAttentionManager.mService = new MockIAttentionService();
+        doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
+    }
+
+    @Test
+    public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() {
+        mSpyAttentionManager.mIsServiceEnabled = false;
+
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+                .isFalse();
+    }
+
+    @Test
+    public void testRegisterProximityUpdates_returnFalseWhenServiceUnavailable() {
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(false).when(mSpyAttentionManager).isServiceAvailable();
+
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+                .isFalse();
+    }
+
+    @Test
+    public void testRegisterProximityUpdates_returnFalseWhenPowerManagerNotInteract()
+            throws RemoteException {
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+        doReturn(false).when(mMockIPowerManager).isInteractive();
+
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+                .isFalse();
+    }
+
+    @Test
+    public void testRegisterProximityUpdates_callOnSuccess() throws RemoteException {
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+        doReturn(true).when(mMockIPowerManager).isInteractive();
+
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+                .isTrue();
+        verify(mMockProximityCallbackInternal, times(1))
+                .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+    }
+
+    @Test
+    public void testRegisterProximityUpdates_callOnSuccessTwiceInARow() throws RemoteException {
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+        doReturn(true).when(mMockIPowerManager).isInteractive();
+
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+                .isTrue();
+
+        ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate;
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+                .isTrue();
+        assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate);
+        verify(mMockProximityCallbackInternal, times(1))
+                .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+    }
+
+    @Test
+    public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+        verifyZeroInteractions(mMockProximityCallbackInternal);
+    }
+
+    @Test
+    public void testUnregisterProximityUpdates_noCrashWhenCallbackMismatched()
+            throws RemoteException {
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+        doReturn(true).when(mMockIPowerManager).isInteractive();
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
+        verify(mMockProximityCallbackInternal, times(1))
+                .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+
+        ProximityCallbackInternal mismatchedCallback = new ProximityCallbackInternal() {
+            @Override
+            public void onProximityUpdate(double distance) {
+                fail("Callback shouldn't have responded.");
+            }
+        };
+        mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback);
+
+        verifyNoMoreInteractions(mMockProximityCallbackInternal);
+    }
+
+    @Test
+    public void testUnregisterProximityUpdates_cancelRegistrationWhenMatched()
+            throws RemoteException {
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+        doReturn(true).when(mMockIPowerManager).isInteractive();
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+
+        assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull();
+    }
+
+    @Test
+    public void testUnregisterProximityUpdates_noCrashWhenTwiceInARow() throws RemoteException {
+        // Attention Service registers proximity updates.
+        mSpyAttentionManager.mIsServiceEnabled = true;
+        doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+        doReturn(true).when(mMockIPowerManager).isInteractive();
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
+        verify(mMockProximityCallbackInternal, times(1))
+                .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+
+        // Attention Service unregisters the proximity update twice in a row.
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityCallbackInternal);
     }
 
     @Test
@@ -127,7 +252,6 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
         mSpyAttentionManager.mCurrentAttentionCheck = null;
 
         AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
@@ -213,6 +337,13 @@
         public void cancelAttentionCheck(IAttentionCallback callback) {
         }
 
+        public void onStartProximityUpdates(IProximityCallback callback) throws RemoteException {
+            callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+        }
+
+        public void onStopProximityUpdates() throws RemoteException {
+        }
+
         public IBinder asBinder() {
             return null;
         }
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 4caa85c..f5a5689 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -81,6 +81,7 @@
     @Mock HysteresisLevels mScreenBrightnessThresholds;
     @Mock Handler mNoOpHandler;
     @Mock HighBrightnessModeController mHbmController;
+    @Mock BrightnessThrottler mBrightnessThrottler;
 
     @Before
     public void setUp() {
@@ -128,12 +129,15 @@
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
-                mContext, mHbmController, mIdleBrightnessMappingStrategy,
+                mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy,
                 AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
         );
 
         when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
         when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT);
+        // Disable brightness throttling by default. Individual tests can enable it as needed.
+        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessThrottler.isThrottled()).thenReturn(false);
 
         // Configure the brightness controller and grab an instance of the sensor listener,
         // through which we can deliver fake (for test) sensor values.
@@ -420,4 +424,47 @@
         assertEquals(600f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON);
         assertEquals(250f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON);
     }
+
+    @Test
+    public void testBrightnessGetsThrottled() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        mController = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return max brightness at 100 lux
+        final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT;
+        final float lux = 100.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux))
+                .thenReturn(lux);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux))
+                .thenReturn(lux);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness);
+
+        // Sensor reads 100 lux. We should get max brightness.
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux));
+        assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+
+        // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
+        final float throttledBrightness = 0.123f;
+        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness);
+        when(mBrightnessThrottler.isThrottled()).thenReturn(true);
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
+                BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */,
+                0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+        assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
+
+        // Remove throttling and notify ABC again
+        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessThrottler.isThrottled()).thenReturn(false);
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
+                BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */,
+                0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+        assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index d441143..0028969 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -165,6 +165,17 @@
                 R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
 
         doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSetMenuLanguage_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSetMenuLanguageEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSetMenuLanguageEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSetMenuLanguageDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecSetMenuLanguageDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecRcProfileTv_userConfigurable);
         doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecRcProfileTvNone_allowed);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index 85d30a6..8e756ae 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -83,6 +83,7 @@
                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
                     HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
@@ -121,6 +122,7 @@
                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
                     HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
@@ -159,6 +161,7 @@
                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
                     HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
                     HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
@@ -317,8 +320,10 @@
     @Test
     public void getDefaultStringValue_MultipleDefaults() {
         setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
-        assertThrows(RuntimeException.class,
-                () -> new HdmiCecConfig(mContext, mStorageAdapter));
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+        assertThat(hdmiCecConfig.getDefaultStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+                .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 7751ef5..c48a974 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -42,6 +42,7 @@
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
 import android.hardware.hdmi.IHdmiControlStatusChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
 import android.os.Binder;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -747,6 +748,114 @@
     }
 
     @Test
+    public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() {
+        int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+        int sourceAddress = Constants.ADDR_TV;
+        byte[] params = {0x00, 0x01, 0x02, 0x03};
+        int vendorId = 0x123456;
+
+        VendorCommandListener vendorCmdListener =
+                new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+        mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage vendorCommandNoId =
+                HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params);
+        mNativeWrapper.onCecMessage(vendorCommandNoId);
+        mTestLooper.dispatchAll();
+        assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+        assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+        assertThat(vendorCmdListener.mHasVendorId).isFalse();
+    }
+
+    @Test
+    public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() {
+        int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+        int sourceAddress = Constants.ADDR_TV;
+        byte[] params = {0x00, 0x01, 0x02, 0x03};
+        int vendorId = 0x123456;
+
+        VendorCommandListener vendorCmdListener =
+                new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+        mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage vendorCommandWithId =
+                HdmiCecMessageBuilder.buildVendorCommandWithId(
+                        sourceAddress, destAddress, vendorId, params);
+        mNativeWrapper.onCecMessage(vendorCommandWithId);
+        mTestLooper.dispatchAll();
+        assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+        assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+        assertThat(vendorCmdListener.mHasVendorId).isTrue();
+    }
+
+    @Test
+    public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() {
+        int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+        int sourceAddress = Constants.ADDR_TV;
+        byte[] params = {0x00, 0x01, 0x02, 0x03};
+        int vendorId = 0x123456;
+        int diffVendorId = 0x345678;
+
+        VendorCommandListener vendorCmdListener =
+                new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+        mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage vendorCommandWithDiffId =
+                HdmiCecMessageBuilder.buildVendorCommandWithId(
+                        sourceAddress, destAddress, diffVendorId, params);
+        mNativeWrapper.onCecMessage(vendorCommandWithDiffId);
+        mTestLooper.dispatchAll();
+        assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse();
+    }
+
+    private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub {
+        boolean mVendorCommandCallbackReceived = false;
+        boolean mParamsCorrect = false;
+        boolean mHasVendorId = false;
+
+        int mSourceAddress;
+        int mDestAddress;
+        byte[] mParams;
+        int mVendorId;
+
+        VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) {
+            this.mSourceAddress = sourceAddress;
+            this.mDestAddress = destAddress;
+            this.mParams = params.clone();
+            this.mVendorId = vendorId;
+        }
+
+        @Override
+        public void onReceived(
+                int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) {
+            mVendorCommandCallbackReceived = true;
+            if (mSourceAddress == sourceAddress && mDestAddress == destAddress) {
+                byte[] expectedParams;
+                if (hasVendorId) {
+                    // If the command has vendor ID, we have to add it to mParams.
+                    expectedParams = new byte[params.length];
+                    expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF);
+                    expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF);
+                    expectedParams[2] = (byte) (mVendorId & 0xFF);
+                    System.arraycopy(mParams, 0, expectedParams, 3, mParams.length);
+                } else {
+                    expectedParams = params.clone();
+                }
+                if (Arrays.equals(expectedParams, params)) {
+                    mParamsCorrect = true;
+                }
+            }
+            mHasVendorId = hasVendorId;
+        }
+
+        @Override
+        public void onControlStateChanged(boolean enabled, int reason) {}
+    }
+
+    @Test
     public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby(
                 Constants.ADDR_TV,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 7542033..7e27e54 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -66,6 +66,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
 import android.widget.RemoteViews;
 
 import androidx.test.filters.SmallTest;
@@ -1304,4 +1305,45 @@
 
         assertFalse(record.isConversation());
     }
+
+    @Test
+    public void mergePhoneNumbers_nulls() {
+        // make sure nothing dies if we just don't have any phone numbers
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, null /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+        // by default, no phone numbers
+        assertNull(record.getPhoneNumbers());
+
+        // nothing happens if we attempt to merge phone numbers but there aren't any
+        record.mergePhoneNumbers(null);
+        assertNull(record.getPhoneNumbers());
+    }
+
+    @Test
+    public void mergePhoneNumbers_addNumbers() {
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, null /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+        // by default, no phone numbers
+        assertNull(record.getPhoneNumbers());
+
+        // make sure it behaves properly when we merge in some real content
+        record.mergePhoneNumbers(new ArraySet<>(
+                new String[]{"16175551212", "16175552121"}));
+        assertTrue(record.getPhoneNumbers().contains("16175551212"));
+        assertTrue(record.getPhoneNumbers().contains("16175552121"));
+        assertFalse(record.getPhoneNumbers().contains("16175553434"));
+
+        // now merge in a new number, make sure old ones are still there and the new one
+        // is also there
+        record.mergePhoneNumbers(new ArraySet<>(new String[]{"16175553434"}));
+        assertTrue(record.getPhoneNumbers().contains("16175551212"));
+        assertTrue(record.getPhoneNumbers().contains("16175552121"));
+        assertTrue(record.getPhoneNumbers().contains("16175553434"));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index 0bf105d..0552a83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -19,8 +19,13 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -29,6 +34,7 @@
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserManager;
@@ -43,6 +49,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -240,6 +248,118 @@
         assertFalse(ContentProvider.uriHasUserId(queryUri.getValue()));
     }
 
+    @Test
+    public void testMergePhoneNumbers_noPhoneNumber() {
+        // If merge phone number is called but the contacts lookup turned up no available
+        // phone number (HAS_PHONE_NUMBER is false), then no query should happen.
+
+        // setup of various bits required for querying
+        final Context mockContext = mock(Context.class);
+        final ContentResolver mockContentResolver = mock(ContentResolver.class);
+        when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+        final int contactId = 12345;
+        final Uri lookupUri = Uri.withAppendedPath(
+                ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId));
+
+        // when the contact is looked up, we return a cursor that has one entry whose info is:
+        //  _ID: 1
+        //  LOOKUP_KEY: "testlookupkey"
+        //  STARRED: 0
+        //  HAS_PHONE_NUMBER: 0
+        Cursor cursor = makeMockCursor(1, "testlookupkey", 0, 0);
+        when(mockContentResolver.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        // call searchContacts and then mergePhoneNumbers, make sure we never actually
+        // query the content resolver for a phone number
+        new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri);
+        verify(mockContentResolver, never()).query(
+                eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
+                eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }),
+                contains(ContactsContract.Contacts.LOOKUP_KEY),
+                any(),  // selection args
+                isNull());  // sort order
+    }
+
+    @Test
+    public void testMergePhoneNumbers_hasNumber() {
+        // If merge phone number is called and the contact lookup has a phone number,
+        // make sure there's then a subsequent query for the phone number.
+
+        // setup of various bits required for querying
+        final Context mockContext = mock(Context.class);
+        final ContentResolver mockContentResolver = mock(ContentResolver.class);
+        when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+        final int contactId = 12345;
+        final Uri lookupUri = Uri.withAppendedPath(
+                ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId));
+
+        // when the contact is looked up, we return a cursor that has one entry whose info is:
+        //  _ID: 1
+        //  LOOKUP_KEY: "testlookupkey"
+        //  STARRED: 0
+        //  HAS_PHONE_NUMBER: 1
+        Cursor cursor = makeMockCursor(1, "testlookupkey", 0, 1);
+
+        // make sure to add some specifics so this cursor is only returned for the
+        // contacts database lookup.
+        when(mockContentResolver.query(eq(lookupUri), any(),
+                isNull(), isNull(), isNull())).thenReturn(cursor);
+
+        // in the case of a phone lookup, return null cursor; that's not an error case
+        // and we're not checking the actual storing of the phone data here.
+        when(mockContentResolver.query(eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
+                eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }),
+                contains(ContactsContract.Contacts.LOOKUP_KEY),
+                any(), isNull())).thenReturn(null);
+
+        // call searchContacts and then mergePhoneNumbers, and check that we query
+        // once for the
+        new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri);
+        verify(mockContentResolver, times(1)).query(
+                eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
+                eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }),
+                contains(ContactsContract.Contacts.LOOKUP_KEY),
+                eq(new String[] { "testlookupkey" }),  // selection args
+                isNull());  // sort order
+    }
+
+    // Creates a cursor that points to one item of Contacts data with the specified
+    // columns.
+    private Cursor makeMockCursor(int id, String lookupKey, int starred, int hasPhone) {
+        Cursor mockCursor = mock(Cursor.class);
+        when(mockCursor.moveToFirst()).thenReturn(true);
+        doAnswer(new Answer<Boolean>() {
+            boolean mAccessed = false;
+            @Override
+            public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                if (!mAccessed) {
+                    mAccessed = true;
+                    return true;
+                }
+                return false;
+            }
+
+        }).when(mockCursor).moveToNext();
+
+        // id
+        when(mockCursor.getColumnIndex(ContactsContract.Contacts._ID)).thenReturn(0);
+        when(mockCursor.getInt(0)).thenReturn(id);
+
+        // lookup key
+        when(mockCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)).thenReturn(1);
+        when(mockCursor.getString(1)).thenReturn(lookupKey);
+
+        // starred
+        when(mockCursor.getColumnIndex(ContactsContract.Contacts.STARRED)).thenReturn(2);
+        when(mockCursor.getInt(2)).thenReturn(starred);
+
+        // has phone number
+        when(mockCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)).thenReturn(3);
+        when(mockCursor.getInt(3)).thenReturn(hasPhone);
+
+        return mockCursor;
+    }
+
     private void assertStringArrayEquals(String message, String[] expected, String[] result) {
         String expectedString = Arrays.toString(expected);
         String resultString = Arrays.toString(result);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index 0f18cc6..abcc8c1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -50,11 +50,13 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.ArraySet;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.server.UiServiceTestCase;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -72,6 +74,8 @@
 
     @Mock private TelephonyManager mTelephonyManager;
 
+    private long mTestStartTime;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -79,6 +83,13 @@
 
         // for repeat callers / matchesCallFilter
         mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
+        mTestStartTime = System.currentTimeMillis();
+    }
+
+    @After
+    public void tearDown() {
+        // make sure to get rid of any data stored in repeat callers
+        mZenModeFiltering.cleanUpCallersAfter(mTestStartTime);
     }
 
     private NotificationRecord getNotificationRecord() {
@@ -108,7 +119,10 @@
         return extras;
     }
 
-    private NotificationRecord getNotificationRecordWithPeople(String[] people) {
+    // Create a notification record with the people String array as the
+    // bundled extras, and the numbers ArraySet as additional phone numbers.
+    private NotificationRecord getRecordWithPeopleInfo(String[] people,
+            ArraySet<String> numbers) {
         // set up notification record
         NotificationRecord r = mock(NotificationRecord.class);
         StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -116,6 +130,7 @@
         notification.extras = makeExtrasBundleWithPeople(people);
         when(sbn.getNotification()).thenReturn(notification);
         when(r.getSbn()).thenReturn(sbn);
+        when(r.getPhoneNumbers()).thenReturn(numbers);
         return r;
     }
 
@@ -339,7 +354,7 @@
         // after calls given an email with an exact string match, make sure that
         // matchesCallFilter returns the right thing
         String[] mailSource = new String[]{"mailto:hello.world"};
-        mZenModeFiltering.recordCall(getNotificationRecordWithPeople(mailSource));
+        mZenModeFiltering.recordCall(getRecordWithPeopleInfo(mailSource, null));
 
         // set up policy to only allow repeat callers
         Policy policy = new Policy(
@@ -362,7 +377,7 @@
         when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
 
         String[] telSource = new String[]{"tel:+1-617-555-1212"};
-        mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource));
+        mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
 
         // set up policy to only allow repeat callers
         Policy policy = new Policy(
@@ -406,7 +421,7 @@
         when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
 
         String[] telSource = new String[]{"tel:%2B16175551212"};
-        mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource));
+        mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
 
         // set up policy to only allow repeat callers
         Policy policy = new Policy(
@@ -419,25 +434,64 @@
         Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"});
         Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"});
 
-        assertTrue("same number should match",
+        assertTrue("same number 1 should match",
                 ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                         policy, UserHandle.SYSTEM,
                         same1, null, 0, 0));
-        assertTrue("same number should match",
+        assertTrue("same number 2 should match",
                 ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                         policy, UserHandle.SYSTEM,
                         same2, null, 0, 0));
-        assertTrue("same number should match",
+        assertTrue("same number 3 should match",
                 ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                         policy, UserHandle.SYSTEM,
                         same3, null, 0, 0));
-        assertFalse("different number should not match",
+        assertFalse("different number 1 should not match",
                 ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                         policy, UserHandle.SYSTEM,
                         different1, null, 0, 0));
-        assertFalse("different number should not match",
+        assertFalse("different number 2 should not match",
                 ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                         policy, UserHandle.SYSTEM,
                         different2, null, 0, 0));
     }
+
+    @Test
+    public void testMatchesCallFilter_repeatCallers_viaRecordPhoneNumbers() {
+        // make sure that phone numbers that are passed in via the NotificationRecord's
+        // cached phone numbers field (from a contact lookup if the record is provided a contact
+        // uri) also get recorded in the repeat callers list.
+
+        // set up telephony manager behavior
+        when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
+
+        String[] contactSource = new String[]{"content://contacts/lookup/uri-here"};
+        ArraySet<String> contactNumbers = new ArraySet<>(
+                new String[]{"1-617-555-1212", "1-617-555-3434"});
+        NotificationRecord record = getRecordWithPeopleInfo(contactSource, contactNumbers);
+        record.mergePhoneNumbers(contactNumbers);
+        mZenModeFiltering.recordCall(record);
+
+        // set up policy to only allow repeat callers
+        Policy policy = new Policy(
+                PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+        // both phone numbers should register here
+        Bundle tel1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"});
+        Bundle tel2 = makeExtrasBundleWithPeople(new String[]{"tel:16175553434"});
+        Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:16175555656"});
+
+        assertTrue("contact number 1 should match",
+                ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                        policy, UserHandle.SYSTEM,
+                        tel1, null, 0, 0));
+        assertTrue("contact number 2 should match",
+                ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                        policy, UserHandle.SYSTEM,
+                        tel2, null, 0, 0));
+        assertFalse("different number should not match",
+                ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                        policy, UserHandle.SYSTEM,
+                        different, null, 0, 0));
+    }
 }
diff --git a/services/wallpapereffectsgeneration/Android.bp b/services/wallpapereffectsgeneration/Android.bp
new file mode 100644
index 0000000..4dbb0fd
--- /dev/null
+++ b/services/wallpapereffectsgeneration/Android.bp
@@ -0,0 +1,22 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "services.wallpapereffectsgeneration-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.wallpapereffectsgeneration",
+    defaults: ["platform_service_defaults"],
+    srcs: [":services.wallpapereffectsgeneration-sources"],
+    libs: ["services.core"],
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java
new file mode 100644
index 0000000..c228daf
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java
@@ -0,0 +1,113 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.wallpapereffectsgeneration.IWallpaperEffectsGenerationService;
+import android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the
+ * {@link android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService}
+ * implementation in another process.
+ */
+public class RemoteWallpaperEffectsGenerationService extends
+        AbstractMultiplePendingRequestsRemoteService<RemoteWallpaperEffectsGenerationService,
+                IWallpaperEffectsGenerationService> {
+
+    private static final String TAG =
+            RemoteWallpaperEffectsGenerationService.class.getSimpleName();
+
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+    private final RemoteWallpaperEffectsGenerationServiceCallback mCallback;
+
+    public RemoteWallpaperEffectsGenerationService(Context context,
+            ComponentName componentName, int userId,
+            RemoteWallpaperEffectsGenerationServiceCallback callback,
+            boolean bindInstantServiceAllowed,
+            boolean verbose) {
+        super(context, WallpaperEffectsGenerationService.SERVICE_INTERFACE,
+                componentName, userId, callback,
+                context.getMainThreadHandler(),
+                bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
+                verbose, /* initialCapacity= */ 1);
+        mCallback = callback;
+    }
+
+    @Override
+    protected IWallpaperEffectsGenerationService getServiceInterface(IBinder service) {
+        return IWallpaperEffectsGenerationService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return PERMANENT_BOUND_TIMEOUT_MS;
+    }
+
+    @Override
+    protected long getRemoteRequestMillis() {
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+    }
+
+    /**
+     * Schedules a request to bind to the remote service.
+     */
+    public void reconnect() {
+        super.scheduleBind();
+    }
+
+    /**
+     * Schedule async request on remote service.
+     */
+    public void scheduleOnResolvedService(
+            @NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) {
+        scheduleAsyncRequest(request);
+    }
+
+    /**
+     * Execute async request on remote service immediately instead of sending it to Handler queue.
+     */
+    public void executeOnResolvedService(
+            @NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) {
+        executeAsyncRequest(request);
+    }
+
+    /**
+     * Notifies server (WallpaperEffectsGenerationPerUserService) about unexpected events..
+     */
+    public interface RemoteWallpaperEffectsGenerationServiceCallback
+            extends VultureCallback<RemoteWallpaperEffectsGenerationService> {
+        /**
+         * Notifies change in connected state of the remote service.
+         */
+        void onConnectedStateChanged(boolean connected);
+    }
+
+    @Override // from AbstractRemoteService
+    protected void handleOnConnectedStateChanged(boolean connected) {
+        if (mCallback != null) {
+            mCallback.onConnectedStateChanged(connected);
+        }
+    }
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java
new file mode 100644
index 0000000..0d0b3e0
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java
@@ -0,0 +1,185 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import static android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.content.Context.WALLPAPER_EFFECTS_GENERATION_SERVICE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
+import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to return wallpaper effect given a request.
+ */
+public class WallpaperEffectsGenerationManagerService extends
+        AbstractMasterSystemService<WallpaperEffectsGenerationManagerService,
+                WallpaperEffectsGenerationPerUserService> {
+    private static final String TAG =
+            WallpaperEffectsGenerationManagerService.class.getSimpleName();
+    private static final boolean DEBUG = false;
+    private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+    private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+    public WallpaperEffectsGenerationManagerService(Context context) {
+        super(context,
+                new FrameworkResourcesServiceNameResolver(context,
+                        com.android.internal.R.string.config_defaultWallpaperEffectsGenerationService),
+                null,
+                PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
+        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+    }
+
+    @Override
+    protected WallpaperEffectsGenerationPerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new WallpaperEffectsGenerationPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(WALLPAPER_EFFECTS_GENERATION_SERVICE,
+                new WallpaperEffectsGenerationManagerStub());
+    }
+
+    @Override
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION, TAG);
+    }
+
+    @Override // from AbstractMasterSystemService
+    protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+        final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            service.onPackageUpdatedLocked();
+        }
+    }
+
+    @Override // from AbstractMasterSystemService
+    protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+        final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            service.onPackageRestartedLocked();
+        }
+    }
+
+    @Override
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMP_SERVICE_DURATION_MS;
+    }
+
+    private class WallpaperEffectsGenerationManagerStub
+            extends IWallpaperEffectsGenerationManager.Stub {
+        @Override
+        public void generateCinematicEffect(@NonNull CinematicEffectRequest request,
+                @NonNull ICinematicEffectListener listener) {
+            if (!runForUserLocked("generateCinematicEffect", (service) ->
+                    service.onGenerateCinematicEffectLocked(request, listener))) {
+                try {
+                    listener.onCinematicEffectGenerated(
+                            new CinematicEffectResponse.Builder(
+                                    CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR,
+                                    request.getTaskId()).build());
+                } catch (RemoteException e) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "fail to invoke cinematic effect listener for task["
+                                + request.getTaskId() + "]");
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) {
+            runForUserLocked("returnCinematicResponse", (service) ->
+                    service.onReturnCinematicEffectResponseLocked(response));
+        }
+
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err,
+                @NonNull String[] args, @Nullable ShellCallback callback,
+                @NonNull ResultReceiver resultReceiver) {
+            new WallpaperEffectsGenerationManagerServiceShellCommand(
+                    WallpaperEffectsGenerationManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+
+        /**
+         * Execute the operation for the user locked. Return true if
+         * WallpaperEffectsGenerationPerUserService is found for the user.
+         * Otherwise return false.
+         */
+        private boolean runForUserLocked(@NonNull final String func,
+                @NonNull final Consumer<WallpaperEffectsGenerationPerUserService> c) {
+            ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+            final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                    Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL,
+                    null, null);
+            if (DEBUG) {
+                Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+            }
+            Context ctx = getContext();
+            if (!(ctx.checkCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION)
+                    == PERMISSION_GRANTED
+                    || mServiceNameResolver.isTemporary(userId)
+                    || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
+                String msg = "Permission Denial: Cannot call " + func + " from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+            final long origId = Binder.clearCallingIdentity();
+            boolean accepted = false;
+            try {
+                synchronized (mLock) {
+                    final WallpaperEffectsGenerationPerUserService service =
+                            getServiceForUserLocked(userId);
+                    if (service != null) {
+                        accepted = true;
+                        c.accept(service);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+
+            return accepted;
+        }
+    }
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java
new file mode 100644
index 0000000..fc6f75f
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the WallpaperEffectsGenerationService.
+ */
+public class WallpaperEffectsGenerationManagerServiceShellCommand extends ShellCommand {
+
+    private static final String TAG =
+            WallpaperEffectsGenerationManagerServiceShellCommand.class.getSimpleName();
+
+    private final WallpaperEffectsGenerationManagerService mService;
+
+    public WallpaperEffectsGenerationManagerServiceShellCommand(
+            @NonNull WallpaperEffectsGenerationManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "set": {
+                final String what = getNextArgRequired();
+                switch (what) {
+                    case "temporary-service": {
+                        final int userId = Integer.parseInt(getNextArgRequired());
+                        String serviceName = getNextArg();
+                        if (serviceName == null) {
+                            mService.resetTemporaryService(userId);
+                            pw.println("WallpaperEffectsGenerationService temporarily reset. ");
+                            return 0;
+                        }
+                        final int duration = Integer.parseInt(getNextArgRequired());
+                        mService.setTemporaryService(userId, serviceName, duration);
+                        pw.println("WallpaperEffectsGenerationService temporarily set to "
+                                + serviceName + " for " + duration + "ms");
+                        break;
+                    }
+                }
+            }
+            break;
+            default:
+                return handleDefaultCommands(cmd);
+        }
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter()) {
+            pw.println("WallpaperEffectsGenerationService commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+            pw.println("    Temporarily (for DURATION ms) changes the service implemtation.");
+            pw.println("    To reset, call with just the USER_ID argument.");
+            pw.println("");
+        }
+    }
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
new file mode 100644
index 0000000..d541051
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
@@ -0,0 +1,274 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link WallpaperEffectsGenerationManagerService}.
+ */
+public class WallpaperEffectsGenerationPerUserService extends
+        AbstractPerUserSystemService<WallpaperEffectsGenerationPerUserService,
+                WallpaperEffectsGenerationManagerService> implements
+        RemoteWallpaperEffectsGenerationService.RemoteWallpaperEffectsGenerationServiceCallback {
+
+    private static final String TAG =
+            WallpaperEffectsGenerationPerUserService.class.getSimpleName();
+
+    @GuardedBy("mLock")
+    private CinematicEffectListenerWrapper mCinematicEffectListenerWrapper;
+
+    @Nullable
+    @GuardedBy("mLock")
+    private RemoteWallpaperEffectsGenerationService mRemoteService;
+
+    protected WallpaperEffectsGenerationPerUserService(
+            WallpaperEffectsGenerationManagerService master,
+            Object lock, int userId) {
+        super(master, lock, userId);
+    }
+
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+        ServiceInfo si;
+        try {
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new NameNotFoundException("Could not get service for " + serviceComponent);
+        }
+        if (!Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE.equals(si.permission)) {
+            Slog.w(TAG, "WallpaperEffectsGenerationService from '" + si.packageName
+                    + "' does not require permission "
+                    + Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE);
+            throw new SecurityException("Service does not require permission "
+                    + Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE);
+        }
+        return si;
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        final boolean enabledChanged = super.updateLocked(disabled);
+        updateRemoteServiceLocked();
+        return enabledChanged;
+    }
+
+    /**
+     * Notifies the service of a new cinematic effect generation request.
+     */
+    @GuardedBy("mLock")
+    public void onGenerateCinematicEffectLocked(
+            @NonNull CinematicEffectRequest cinematicEffectRequest,
+            @NonNull ICinematicEffectListener cinematicEffectListener) {
+        String newTaskId = cinematicEffectRequest.getTaskId();
+        // Previous request is still being processed.
+        if (mCinematicEffectListenerWrapper != null) {
+            if (mCinematicEffectListenerWrapper.mTaskId.equals(newTaskId)) {
+                invokeCinematicListenerAndCleanup(
+                        new CinematicEffectResponse.Builder(
+                                CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING, newTaskId)
+                                .build()
+                );
+            } else {
+                invokeCinematicListenerAndCleanup(
+                        new CinematicEffectResponse.Builder(
+                                CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS,
+                                newTaskId).build()
+                );
+            }
+            return;
+        }
+        RemoteWallpaperEffectsGenerationService remoteService = ensureRemoteServiceLocked();
+        if (remoteService != null) {
+            remoteService.executeOnResolvedService(
+                    s -> s.onGenerateCinematicEffect(cinematicEffectRequest));
+            mCinematicEffectListenerWrapper =
+                    new CinematicEffectListenerWrapper(newTaskId, cinematicEffectListener);
+        } else {
+            if (isDebug()) {
+                Slog.d(TAG, "Remote service not found");
+            }
+            try {
+                cinematicEffectListener.onCinematicEffectGenerated(
+                        createErrorCinematicEffectResponse(newTaskId));
+            } catch (RemoteException e) {
+                if (isDebug()) {
+                    Slog.d(TAG, "Failed to invoke cinematic effect listener for task [" + newTaskId
+                            + "]");
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies the service of a generated cinematic effect response.
+     */
+    @GuardedBy("mLock")
+    public void onReturnCinematicEffectResponseLocked(
+            @NonNull CinematicEffectResponse cinematicEffectResponse) {
+        invokeCinematicListenerAndCleanup(cinematicEffectResponse);
+    }
+
+    @GuardedBy("mLock")
+    private void updateRemoteServiceLocked() {
+        if (mRemoteService != null) {
+            mRemoteService.destroy();
+            mRemoteService = null;
+        }
+        // End existing response and clean up listener for next request.
+        if (mCinematicEffectListenerWrapper != null) {
+            invokeCinematicListenerAndCleanup(
+                    createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId));
+        }
+
+    }
+
+    void onPackageUpdatedLocked() {
+        if (isDebug()) {
+            Slog.v(TAG, "onPackageUpdatedLocked()");
+        }
+        destroyAndRebindRemoteService();
+    }
+
+    void onPackageRestartedLocked() {
+        if (isDebug()) {
+            Slog.v(TAG, "onPackageRestartedLocked()");
+        }
+        destroyAndRebindRemoteService();
+    }
+
+    private void destroyAndRebindRemoteService() {
+        if (mRemoteService == null) {
+            return;
+        }
+
+        if (isDebug()) {
+            Slog.d(TAG, "Destroying the old remote service.");
+        }
+        mRemoteService.destroy();
+        mRemoteService = null;
+        mRemoteService = ensureRemoteServiceLocked();
+        if (mRemoteService != null) {
+            if (isDebug()) {
+                Slog.d(TAG, "Rebinding to the new remote service.");
+            }
+            mRemoteService.reconnect();
+        }
+        // Clean up listener for next request.
+        if (mCinematicEffectListenerWrapper != null) {
+            invokeCinematicListenerAndCleanup(
+                    createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId));
+        }
+    }
+
+    private CinematicEffectResponse createErrorCinematicEffectResponse(String taskId) {
+        return new CinematicEffectResponse.Builder(
+                CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR,
+                taskId).build();
+    }
+
+    @GuardedBy("mLock")
+    private void invokeCinematicListenerAndCleanup(
+            CinematicEffectResponse cinematicEffectResponse) {
+        try {
+            if (mCinematicEffectListenerWrapper != null
+                    && mCinematicEffectListenerWrapper.mListener != null) {
+                mCinematicEffectListenerWrapper.mListener.onCinematicEffectGenerated(
+                        cinematicEffectResponse);
+            } else {
+                if (isDebug()) {
+                    Slog.w(TAG, "Cinematic effect listener not found for task["
+                            + mCinematicEffectListenerWrapper.mTaskId + "]");
+                }
+            }
+        } catch (RemoteException e) {
+            if (isDebug()) {
+                Slog.w(TAG, "Error invoking cinematic effect listener for task["
+                        + mCinematicEffectListenerWrapper.mTaskId + "]");
+            }
+        } finally {
+            mCinematicEffectListenerWrapper = null;
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteWallpaperEffectsGenerationService ensureRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "ensureRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteWallpaperEffectsGenerationService(getContext(),
+                    serviceComponent, mUserId, this,
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
+        return mRemoteService;
+    }
+
+    @Override // from RemoteWallpaperEffectsGenerationService
+    public void onServiceDied(RemoteWallpaperEffectsGenerationService service) {
+        Slog.w(TAG, "remote wallpaper effects generation service died");
+        updateRemoteServiceLocked();
+    }
+
+    @Override // from RemoteWallpaperEffectsGenerationService
+    public void onConnectedStateChanged(boolean connected) {
+        if (!connected) {
+            Slog.w(TAG, "remote wallpaper effects generation service disconnected");
+            updateRemoteServiceLocked();
+        }
+    }
+
+    private static final class CinematicEffectListenerWrapper {
+        @NonNull
+        private final String mTaskId;
+        @NonNull
+        private final ICinematicEffectListener mListener;
+
+        CinematicEffectListenerWrapper(
+                @NonNull final String taskId,
+                @NonNull final ICinematicEffectListener listener) {
+            mTaskId = taskId;
+            mListener = listener;
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e0e7913..3c277b7 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4540,9 +4540,7 @@
      * Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
      * subscription from a group instead of adding it to a group.
      *
-     * TODO: Expose in a future release.
-     *
-     * @hide
+     * <p>This value will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
      */
     public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
 
@@ -4555,9 +4553,7 @@
      * <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from
      * its current group.
      *
-     * TODO: unhide this key.
-     *
-     * @hide
+     * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
      */
     public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING =
             "subscription_group_uuid_string";
@@ -4605,17 +4601,15 @@
             "data_switch_validation_min_gap_long";
 
     /**
-    * A boolean property indicating whether this subscription should be managed as an opportunistic
-    * subscription.
-    *
-    * If true, then this subscription will be selected based on available coverage and will not be
-    * available for a user in settings menus for selecting macro network providers. If unset,
-    * defaults to “false”.
-    *
-    * TODO: unhide this key.
-    *
-    * @hide
-    */
+     * A boolean property indicating whether this subscription should be managed as an opportunistic
+     * subscription.
+     *
+     * If true, then this subscription will be selected based on available coverage and will not be
+     * available for a user in settings menus for selecting macro network providers. If unset,
+     * defaults to “false”.
+     *
+     * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+     */
     public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL =
             "is_opportunistic_subscription_bool";
 
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
index 48bfd6f..6290292 100644
--- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -11,10 +11,20 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import com.google.common.truth.Truth.assertThat
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
 import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
-import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
 import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
 import java.lang.IllegalArgumentException
+import java.io.ByteArrayOutputStream
+import java.security.KeyPairGenerator
+import java.security.KeyStore
 import java.time.Duration
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.TimeUnit
@@ -23,25 +33,26 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SystemAttestationVerificationTest {
-
     @get:Rule
     val rule = ActivityScenarioRule(TestActivity::class.java)
 
     private lateinit var activity: Activity
     private lateinit var avm: AttestationVerificationManager
+    private lateinit var androidKeystore: KeyStore
 
     @Before
     fun setup() {
         rule.getScenario().onActivity {
             avm = it.getSystemService(AttestationVerificationManager::class.java)
             activity = it
+            androidKeystore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
         }
     }
 
     @Test
     fun verifyAttestation_returnsUnknown() {
         val future = CompletableFuture<Int>()
-        val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        val profile = AttestationProfile(PROFILE_PEER_DEVICE)
         avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
                 activity.mainExecutor) { result, _ ->
             future.complete(result)
@@ -51,9 +62,82 @@
     }
 
     @Test
-    fun verifyToken_returnsUnknown() {
+    fun verifyAttestation_returnsFailureWithEmptyAttestation() {
         val future = CompletableFuture<Int>()
         val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0),
+            activity.mainExecutor) { result, _ ->
+            future.complete(result)
+        }
+
+        assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+    }
+
+    @Test
+    fun verifyAttestation_returnsFailureWithEmptyRequirements() {
+        val future = CompletableFuture<Int>()
+        val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+        avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+            Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+            future.complete(result)
+        }
+        assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+    }
+
+    @Test
+    fun verifyAttestation_returnsFailureWithWrongBindingType() {
+        val future = CompletableFuture<Int>()
+        val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+        avm.verifyAttestation(selfTrusted.profile, TYPE_PUBLIC_KEY,
+            selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+            future.complete(result)
+        }
+        assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+    }
+
+    @Test
+    fun verifyAttestation_returnsFailureWithWrongRequirements() {
+        val future = CompletableFuture<Int>()
+        val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+        val wrongKeyRequirements = Bundle()
+        wrongKeyRequirements.putByteArray(
+            "wrongBindingKey", "challengeStr".encodeToByteArray())
+        avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+            wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+            future.complete(result)
+        }
+        assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+    }
+
+    @Test
+    fun verifyAttestation_returnsFailureWithWrongChallenge() {
+        val future = CompletableFuture<Int>()
+        val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+        val wrongChallengeRequirements = Bundle()
+        wrongChallengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
+        avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+            wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) {
+                result, _ -> future.complete(result)
+        }
+        assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+    }
+
+    // TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED.
+    @Test
+    fun verifyAttestation_returnsSuccess() {
+        val future = CompletableFuture<Int>()
+        val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+        avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+            selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+            future.complete(result)
+        }
+        assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS)
+    }
+
+    @Test
+    fun verifyToken_returnsUnknown() {
+        val future = CompletableFuture<Int>()
+        val profile = AttestationProfile(PROFILE_PEER_DEVICE)
         avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
                 activity.mainExecutor) { _, token ->
             val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
@@ -66,7 +150,7 @@
     @Test
     fun verifyToken_tooBigMaxAgeThrows() {
         val future = CompletableFuture<VerificationToken>()
-        val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        val profile = AttestationProfile(PROFILE_PEER_DEVICE)
         avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
                 activity.mainExecutor) { _, token ->
             future.complete(token)
@@ -87,4 +171,52 @@
             super.onCreate(savedInstanceState)
         }
     }
+
+    inner class TestSelfTrustedAttestation(val alias: String, val challenge: String) {
+        val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+        val localBindingType = TYPE_CHALLENGE
+        val requirements: Bundle
+        val attestation: ByteArray
+
+        init {
+            val challengeByteArray = challenge.encodeToByteArray()
+            generateAndStoreKey(alias, challengeByteArray)
+            attestation = generateCertificatesByteArray(alias)
+            requirements = Bundle()
+            requirements.putByteArray(PARAM_CHALLENGE, challengeByteArray)
+        }
+
+        private fun generateAndStoreKey(alias: String, challenge: ByteArray) {
+            val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
+                KeyProperties.KEY_ALGORITHM_EC,
+                ANDROID_KEYSTORE
+            )
+            val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
+                alias,
+                KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
+            ).run {
+                // a challenge results in a generated attestation
+                setAttestationChallenge(challenge)
+                setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+                build()
+            }
+            kpg.initialize(parameterSpec)
+            kpg.generateKeyPair()
+        }
+
+        private fun generateCertificatesByteArray(alias: String): ByteArray {
+            val pkEntry = androidKeystore.getEntry(alias, null) as KeyStore.PrivateKeyEntry
+            val certs = pkEntry.certificateChain
+            val bos = ByteArrayOutputStream()
+            certs.forEach {
+                bos.write(it.encoded)
+            }
+            return bos.toByteArray()
+        }
+    }
+
+    companion object {
+        private const val TAG = "AVFTEST"
+        private const val ANDROID_KEYSTORE = "AndroidKeyStore"
+    }
 }
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 41f73cd..228520e 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -18,6 +18,7 @@
         "java/**/*.kt",
     ],
     platform_apis: true,
+    defaults: ["framework-connectivity-test-defaults"],
     test_suites: ["device-tests"],
     certificate: "platform",
     static_libs: [
@@ -28,6 +29,7 @@
         "net-tests-utils",
         "platform-test-annotations",
         "services.core",
+        "service-connectivity-tiramisu-pre-jarjar",
     ],
     libs: [
         "android.test.runner",
diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml
index 2ad9aac..a8f657c 100644
--- a/tests/vcn/AndroidManifest.xml
+++ b/tests/vcn/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.frameworks.tests.vcn">
-
+    <uses-sdk android:minSdkVersion="33"
+        android:targetSdkVersion="33"/>
     <application>
         <uses-library android:name="android.test.runner" />
     </application>