Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 175fb38..65b2511 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -22,7 +22,7 @@
 
 strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
 
-hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
 hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 23dc720..bc3f131 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -96,6 +96,8 @@
     ],
     api_levels_annotations_enabled: false,
     filter_packages: packages_to_document,
+    defaults_visibility: ["//visibility:private"],
+    visibility: ["//frameworks/base/api"],
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -352,6 +354,8 @@
         tag: ".jar",
         dest: "android-non-updatable.jar",
     },
+    defaults_visibility: ["//visibility:private"],
+    visibility: ["//visibility:private"],
 }
 
 java_library_static {
@@ -405,6 +409,8 @@
     system_modules: "none",
     java_version: "1.8",
     compile_dex: true,
+    defaults_visibility: ["//visibility:private"],
+    visibility: ["//visibility:public"],
 }
 
 java_defaults {
@@ -417,6 +423,7 @@
         tag: ".jar",
         dest: "android.jar",
     },
+    defaults_visibility: ["//frameworks/base/services"],
 }
 
 java_library_static {
@@ -516,6 +523,7 @@
         "metalava-manual",
     ],
     args: priv_apps,
+    visibility: ["//visibility:private"],
 }
 
 java_library_static {
@@ -525,4 +533,5 @@
     srcs: [
         ":hwbinder-stubs-docs",
     ],
+    visibility: ["//visibility:public"],
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index 35cea3e..6c62426 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -26,6 +26,7 @@
 
 import java.util.Collections;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Provides results for AppSearch batch operations which encompass multiple documents.
@@ -180,7 +181,7 @@
         public Builder<KeyType, ValueType> setSuccess(
                 @NonNull KeyType key, @Nullable ValueType result) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(key);
+            Objects.requireNonNull(key);
             return setResult(key, AppSearchResult.newSuccessfulResult(result));
         }
 
@@ -198,7 +199,7 @@
                 @AppSearchResult.ResultCode int resultCode,
                 @Nullable String errorMessage) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(key);
+            Objects.requireNonNull(key);
             return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
         }
 
@@ -214,8 +215,8 @@
         public Builder<KeyType, ValueType> setResult(
                 @NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
-            Preconditions.checkNotNull(key);
-            Preconditions.checkNotNull(result);
+            Objects.requireNonNull(key);
+            Objects.requireNonNull(result);
             if (result.isSuccess()) {
                 mSuccesses.put(key, result.getResultValue());
                 mFailures.remove(key);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 693acf1..f6f5c98 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -54,7 +54,6 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
@@ -181,10 +180,10 @@
                 int schemaVersion,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(schemaBundles);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(schemaBundles);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -230,9 +229,9 @@
                 @NonNull String databaseName,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -257,9 +256,9 @@
                 @NonNull String databaseName,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -285,10 +284,10 @@
                 @UserIdInt int userId,
                 @ElapsedRealtimeLong long binderCallStartTimeMillis,
                 @NonNull IAppSearchBatchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(documentBundles);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(documentBundles);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -359,11 +358,11 @@
                 @NonNull Map<String, List<String>> typePropertyPaths,
                 @UserIdInt int userId,
                 @NonNull IAppSearchBatchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(namespace);
-            Preconditions.checkNotNull(uris);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(namespace);
+            Objects.requireNonNull(uris);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -404,11 +403,11 @@
                 @NonNull Bundle searchSpecBundle,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(queryExpression);
-            Preconditions.checkNotNull(searchSpecBundle);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(queryExpression);
+            Objects.requireNonNull(searchSpecBundle);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -439,10 +438,10 @@
                 @NonNull Bundle searchSpecBundle,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(queryExpression);
-            Preconditions.checkNotNull(searchSpecBundle);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(queryExpression);
+            Objects.requireNonNull(searchSpecBundle);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -471,7 +470,7 @@
                 long nextPageToken,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
@@ -644,10 +643,10 @@
                 @NonNull List<String> uris,
                 @UserIdInt int userId,
                 @NonNull IAppSearchBatchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(uris);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(uris);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -684,11 +683,11 @@
                 @NonNull Bundle searchSpecBundle,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(queryExpression);
-            Preconditions.checkNotNull(searchSpecBundle);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(queryExpression);
+            Objects.requireNonNull(searchSpecBundle);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -717,9 +716,9 @@
                 @NonNull String databaseName,
                 @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(packageName);
-            Preconditions.checkNotNull(databaseName);
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(packageName);
+            Objects.requireNonNull(databaseName);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -757,7 +756,7 @@
 
         @Override
         public void initialize(@UserIdInt int userId, @NonNull IAppSearchResultCallback callback) {
-            Preconditions.checkNotNull(callback);
+            Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
             EXECUTOR.execute(() -> {
@@ -788,7 +787,7 @@
         }
 
         private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) {
-            Preconditions.checkNotNull(callingPackage);
+            Objects.requireNonNull(callingPackage);
             if (mPackageManagerInternal.getPackageUid(
                             callingPackage, /*flags=*/ 0, UserHandle.getUserId(callingUid))
                     != callingUid) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index c1b8294..4de52fb 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -35,7 +35,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
 
 import java.util.ArrayList;
@@ -43,6 +42,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -333,9 +333,9 @@
             @NonNull Set<String> schemasNotPlatformSurfaceable,
             @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible)
             throws AppSearchException {
-        Preconditions.checkNotNull(prefix);
-        Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
-        Preconditions.checkNotNull(schemasPackageAccessible);
+        Objects.requireNonNull(prefix);
+        Objects.requireNonNull(schemasNotPlatformSurfaceable);
+        Objects.requireNonNull(schemasPackageAccessible);
 
         // Persist the document
         GenericDocument.Builder<?> visibilityDocument =
@@ -383,8 +383,8 @@
     /** Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. */
     public boolean isSchemaSearchableByCaller(
             @NonNull String prefix, @NonNull String prefixedSchema, int callerUid) {
-        Preconditions.checkNotNull(prefix);
-        Preconditions.checkNotNull(prefixedSchema);
+        Objects.requireNonNull(prefix);
+        Objects.requireNonNull(prefixedSchema);
 
         // We compare appIds here rather than direct uids because the package's uid may change based
         // on the user that's running.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 1c04d99..731ab35 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -29,7 +29,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
 import com.android.server.appsearch.external.localstorage.AppSearchLogger;
 import com.android.server.appsearch.external.localstorage.stats.CallStats;
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
@@ -38,6 +37,7 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Random;
 
 /**
@@ -162,15 +162,15 @@
      * Westworld constructor
      */
     public PlatformLogger(@NonNull Context context, int userId, @NonNull Config config) {
-        mContext = Preconditions.checkNotNull(context);
-        mConfig = Preconditions.checkNotNull(config);
+        mContext = Objects.requireNonNull(context);
+        mConfig = Objects.requireNonNull(config);
         mUserId = userId;
     }
 
     /** Logs {@link CallStats}. */
     @Override
     public void logStats(@NonNull CallStats stats) {
-        Preconditions.checkNotNull(stats);
+        Objects.requireNonNull(stats);
         synchronized (mLock) {
             if (shouldLogForTypeLocked(stats.getCallType())) {
                 logStatsImplLocked(stats);
@@ -181,7 +181,7 @@
     /** Logs {@link PutDocumentStats}. */
     @Override
     public void logStats(@NonNull PutDocumentStats stats) {
-        Preconditions.checkNotNull(stats);
+        Objects.requireNonNull(stats);
         synchronized (mLock) {
             if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) {
                 logStatsImplLocked(stats);
@@ -197,7 +197,7 @@
     */
     public int removeCachedUidForPackage(@NonNull String packageName) {
         // TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         synchronized (mLock) {
             Integer uid = mPackageUidCacheLocked.remove(packageName);
             return uid != null ? uid : Process.INVALID_UID;
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index f0de496..6193367 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -40,11 +40,11 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -81,8 +81,8 @@
 
     private AppSearchSessionShimImpl(
             @NonNull AppSearchSession session, @NonNull ExecutorService executor) {
-        mAppSearchSession = Preconditions.checkNotNull(session);
-        mExecutor = Preconditions.checkNotNull(executor);
+        mAppSearchSession = Objects.requireNonNull(session);
+        mExecutor = Objects.requireNonNull(executor);
     }
 
     @Override
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index 5042ce0..c35849d 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -30,11 +30,11 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -69,8 +69,8 @@
 
     private GlobalSearchSessionShimImpl(
             @NonNull GlobalSearchSession session, @NonNull ExecutorService executor) {
-        mGlobalSearchSession = Preconditions.checkNotNull(session);
-        mExecutor = Preconditions.checkNotNull(executor);
+        mGlobalSearchSession = Objects.requireNonNull(session);
+        mExecutor = Objects.requireNonNull(executor);
     }
 
     @NonNull
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
index 5f26e8c..72078f8 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
@@ -22,12 +22,12 @@
 import android.app.appsearch.SearchResults;
 import android.app.appsearch.SearchResultsShim;
 
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -40,8 +40,8 @@
     private final SearchResults mSearchResults;
 
     SearchResultsShimImpl(@NonNull SearchResults searchResults, @NonNull Executor executor) {
-        mExecutor = Preconditions.checkNotNull(executor);
-        mSearchResults = Preconditions.checkNotNull(searchResults);
+        mExecutor = Objects.requireNonNull(executor);
+        mSearchResults = Objects.requireNonNull(searchResults);
     }
 
     @NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 31da201..03d9a96 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -1684,17 +1684,13 @@
             }
         }
 
-        if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
-            // Do not support windows for idle-until alarms.
-            windowLength = AlarmManager.WINDOW_EXACT;
-        }
-
         // Snap the window to reasonable limits.
         if (windowLength > INTERVAL_DAY) {
             Slog.w(TAG, "Window length " + windowLength
                     + "ms suspiciously long; limiting to 1 day");
             windowLength = INTERVAL_DAY;
-        } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW) {
+        } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW
+                && (flags & FLAG_PRIORITIZE) == 0) {
             if (CompatChanges.isChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS,
                     callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
                 Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
@@ -1741,7 +1737,7 @@
         final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
 
         final long maxElapsed;
-        if (windowLength == AlarmManager.WINDOW_EXACT) {
+        if (windowLength == 0) {
             maxElapsed = triggerElapsed;
         } else if (windowLength < 0) {
             maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
@@ -2145,17 +2141,63 @@
                         + " does not belong to the calling uid " + callingUid);
             }
 
-            final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
-            final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT);
+            // Repeating alarms must use PendingIntent, not direct listener
+            if (interval != 0 && directReceiver != null) {
+                throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
+            }
 
-            // make sure the caller is allowed to use the requested kind of alarm, and also
+            if (workSource != null) {
+                getContext().enforcePermission(
+                        android.Manifest.permission.UPDATE_DEVICE_STATS,
+                        Binder.getCallingPid(), callingUid, "AlarmManager.set");
+            }
+
+            if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+                // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
+                // manager when to come out of idle mode, which is only for DeviceIdleController.
+                if (callingUid != Process.SYSTEM_UID) {
+                    // TODO (b/169463012): Throw instead of tolerating this mistake.
+                    flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
+                } else {
+                    // Do not support windows for idle-until alarms.
+                    windowLength = 0;
+                }
+            }
+
+            // Remove flags reserved for the service, we will apply those later as appropriate.
+            flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
+                    | FLAG_ALLOW_WHILE_IDLE_COMPAT);
+
+            // If this alarm is for an alarm clock, then it must be exact and we will
+            // use it to wake early from idle if needed.
+            if (alarmClock != null) {
+                flags |= FLAG_WAKE_FROM_IDLE;
+                windowLength = 0;
+
+            // If the caller is a core system component or on the user's allowlist, and not calling
+            // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
+            // This means we will allow these alarms to go off as normal even while idle, with no
+            // timing restrictions.
+            } else if (workSource == null && (UserHandle.isCore(callingUid)
+                    || UserHandle.isSameApp(callingUid, mSystemUiUid)
+                    || ((mAppStateTracker != null)
+                    && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
+                flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+                flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_PRIORITIZE);
+            }
+
+            final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
+            final boolean exact = (windowLength == 0);
+
+            // Make sure the caller is allowed to use the requested kind of alarm, and also
             // decide what quota and broadcast options to use.
             Bundle idleOptions = null;
             if ((flags & FLAG_PRIORITIZE) != 0) {
                 getContext().enforcePermission(
                         Manifest.permission.SCHEDULE_PRIORITIZED_ALARM,
                         Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
-                flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT);
+                // The API doesn't allow using both together.
+                flags &= ~FLAG_ALLOW_WHILE_IDLE;
             } else if (exact || allowWhileIdle) {
                 final boolean needsPermission;
                 boolean lowerQuota;
@@ -2193,55 +2235,11 @@
                 }
             }
 
-            // Repeating alarms must use PendingIntent, not direct listener
-            if (interval != 0) {
-                if (directReceiver != null) {
-                    throw new IllegalArgumentException(
-                            "Repeating alarms cannot use AlarmReceivers");
-                }
-            }
-
-            if (workSource != null) {
-                getContext().enforcePermission(
-                        android.Manifest.permission.UPDATE_DEVICE_STATS,
-                        Binder.getCallingPid(), callingUid, "AlarmManager.set");
-            }
-
-            // No incoming callers can request either WAKE_FROM_IDLE or
-            // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
-            flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
-
-            // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
-            // manager when to come out of idle mode, which is only for DeviceIdleController.
-            if (callingUid != Process.SYSTEM_UID) {
-                flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
-            }
-
             // If this is an exact time alarm, then it can't be batched with other alarms.
-            if (windowLength == AlarmManager.WINDOW_EXACT) {
+            if (exact) {
                 flags |= AlarmManager.FLAG_STANDALONE;
             }
 
-            // If this alarm is for an alarm clock, then it must be standalone and we will
-            // use it to wake early from idle if needed.
-            if (alarmClock != null) {
-                flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
-
-            // If the caller is a core system component or on the user's whitelist, and not calling
-            // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
-            // This means we will allow these alarms to go off as normal even while idle, with no
-            // timing restrictions.
-            } else if (workSource == null && (UserHandle.isCore(callingUid)
-                    || UserHandle.isSameApp(callingUid, mSystemUiUid)
-                    || ((mAppStateTracker != null)
-                        && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
-                flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
-                flags &= ~FLAG_ALLOW_WHILE_IDLE;
-                flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
-                flags &= ~FLAG_PRIORITIZE;
-                idleOptions = null;
-            }
-
             setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
                     listenerTag, flags, workSource, alarmClock, callingUid, callingPackage,
                     idleOptions);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index c37d2c3..9b1b066 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -16,7 +16,6 @@
 
 package com.android.server.alarm;
 
-import static com.android.server.alarm.AlarmManagerService.TAG;
 import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
 import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
 
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 667f5ab..1815661 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2234,7 +2234,8 @@
     }
 
     /** Returns true if both the calling and source users for the job are started. */
-    private boolean areUsersStartedLocked(final JobStatus job) {
+    @GuardedBy("mLock")
+    public boolean areUsersStartedLocked(final JobStatus job) {
         boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
         if (job.getUserId() == job.getSourceUserId()) {
             return sourceStarted;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
index 999c53f..12d9c7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -34,6 +34,7 @@
 import android.util.SparseArrayMap;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.job.JobSchedulerService;
 
 import java.util.Objects;
@@ -58,13 +59,28 @@
                 return;
             }
             switch (action) {
+                case Intent.ACTION_PACKAGE_ADDED:
+                    if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                        // Only do this for app updates since new installs won't have any jobs
+                        // scheduled.
+                        final Uri uri = intent.getData();
+                        final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+                        if (pkg != null) {
+                            final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                            final int userId = UserHandle.getUserId(pkgUid);
+                            updateComponentStateForPackage(userId, pkg);
+                        }
+                    }
+                    break;
                 case Intent.ACTION_PACKAGE_CHANGED:
                     final Uri uri = intent.getData();
                     final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
                     final String[] changedComponents = intent.getStringArrayExtra(
                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
                     if (pkg != null && changedComponents != null && changedComponents.length > 0) {
-                        updateComponentStateForPackage(pkg);
+                        final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                        final int userId = UserHandle.getUserId(pkgUid);
+                        updateComponentStateForPackage(userId, pkg);
                     }
                     break;
                 case Intent.ACTION_USER_UNLOCKED:
@@ -86,6 +102,7 @@
         super(service);
 
         final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         mContext.registerReceiverAsUser(
@@ -99,6 +116,7 @@
     }
 
     @Override
+    @GuardedBy("mLock")
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
         updateComponentEnabledStateLocked(jobStatus);
     }
@@ -108,30 +126,53 @@
             boolean forUpdate) {
     }
 
+    @Override
+    @GuardedBy("mLock")
+    public void onAppRemovedLocked(String packageName, int uid) {
+        clearComponentsForPackageLocked(UserHandle.getUserId(uid), packageName);
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    public void onUserRemovedLocked(int userId) {
+        mServiceInfoCache.delete(userId);
+    }
+
     @Nullable
-    private ServiceInfo getServiceInfo(JobStatus jobStatus) {
+    @GuardedBy("mLock")
+    private ServiceInfo getServiceInfoLocked(JobStatus jobStatus) {
         final ComponentName service = jobStatus.getServiceComponent();
         final int userId = jobStatus.getUserId();
-        ServiceInfo si = mServiceInfoCache.get(userId, service);
-        if (si == null) {
-            try {
-                // createContextAsUser may potentially be expensive
-                // TODO: cache user context or improve ContextImpl implementation if this becomes
-                // a problem
-                si = mContext.createContextAsUser(UserHandle.of(userId), 0)
-                        .getPackageManager()
-                        .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO);
-            } catch (NameNotFoundException e) {
-                Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName());
-                return null;
-            }
-            mServiceInfoCache.add(userId, service, si);
+        if (mServiceInfoCache.contains(userId, service)) {
+            // Return whatever is in the cache, even if it's null. When something changes, we
+            // clear the cache.
+            return mServiceInfoCache.get(userId, service);
         }
+
+        ServiceInfo si;
+        try {
+            // createContextAsUser may potentially be expensive
+            // TODO: cache user context or improve ContextImpl implementation if this becomes
+            // a problem
+            si = mContext.createContextAsUser(UserHandle.of(userId), 0)
+                    .getPackageManager()
+                    .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO);
+        } catch (NameNotFoundException e) {
+            if (mService.areUsersStartedLocked(jobStatus)) {
+                // User is fully unlocked but PM still says the package doesn't exist.
+                Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName());
+            }
+            // Write null to the cache so we don't keep querying PM.
+            si = null;
+        }
+        mServiceInfoCache.add(userId, service, si);
+
         return si;
     }
 
+    @GuardedBy("mLock")
     private boolean updateComponentEnabledStateLocked(JobStatus jobStatus) {
-        final ServiceInfo service = getServiceInfo(jobStatus);
+        final ServiceInfo service = getServiceInfoLocked(jobStatus);
 
         if (DEBUG && service == null) {
             Slog.v(TAG, jobStatus.toShortString() + " component not present");
@@ -141,20 +182,26 @@
         return !Objects.equals(ogService, service);
     }
 
-    private void updateComponentStateForPackage(final String pkg) {
-        synchronized (mLock) {
-            for (int u = mServiceInfoCache.numMaps() - 1; u >= 0; --u) {
-                final int userId = mServiceInfoCache.keyAt(u);
-
-                for (int c = mServiceInfoCache.numElementsForKey(userId) - 1; c >= 0; --c) {
-                    final ComponentName cn = mServiceInfoCache.keyAt(u, c);
-                    if (cn.getPackageName().equals(pkg)) {
-                        mServiceInfoCache.delete(userId, cn);
-                    }
-                }
+    @GuardedBy("mLock")
+    private void clearComponentsForPackageLocked(final int userId, final String pkg) {
+        final int uIdx = mServiceInfoCache.indexOfKey(userId);
+        for (int c = mServiceInfoCache.numElementsForKey(userId) - 1; c >= 0; --c) {
+            final ComponentName cn = mServiceInfoCache.keyAt(uIdx, c);
+            if (cn.getPackageName().equals(pkg)) {
+                mServiceInfoCache.delete(userId, cn);
             }
-            updateComponentStatesLocked(
-                    jobStatus -> jobStatus.getServiceComponent().getPackageName().equals(pkg));
+        }
+    }
+
+    private void updateComponentStateForPackage(final int userId, final String pkg) {
+        synchronized (mLock) {
+            clearComponentsForPackageLocked(userId, pkg);
+            updateComponentStatesLocked(jobStatus -> {
+                // Using user ID instead of source user ID because the service will run under the
+                // user ID, not source user ID.
+                return jobStatus.getUserId() == userId
+                        && jobStatus.getServiceComponent().getPackageName().equals(pkg);
+            });
         }
     }
 
@@ -169,6 +216,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void updateComponentStatesLocked(@NonNull Predicate<JobStatus> filter) {
         mComponentStateUpdateFunctor.reset();
         mService.getJobStore().forEachJob(filter, mComponentStateUpdateFunctor);
@@ -178,24 +226,40 @@
     }
 
     final class ComponentStateUpdateFunctor implements Consumer<JobStatus> {
+        @GuardedBy("mLock")
         boolean mChanged;
 
         @Override
+        @GuardedBy("mLock")
         public void accept(JobStatus jobStatus) {
             mChanged |= updateComponentEnabledStateLocked(jobStatus);
         }
 
+        @GuardedBy("mLock")
         private void reset() {
             mChanged = false;
         }
     }
 
     @Override
+    @GuardedBy("mLock")
     public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
-
+        for (int u = 0; u < mServiceInfoCache.numMaps(); ++u) {
+            final int userId = mServiceInfoCache.keyAt(u);
+            for (int p = 0; p < mServiceInfoCache.numElementsForKey(userId); ++p) {
+                final ComponentName componentName = mServiceInfoCache.keyAt(u, p);
+                pw.print(userId);
+                pw.print("-");
+                pw.print(componentName);
+                pw.print(": ");
+                pw.print(mServiceInfoCache.valueAt(u, p));
+                pw.println();
+            }
+        }
     }
 
     @Override
+    @GuardedBy("mLock")
     public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
             Predicate<JobStatus> predicate) {
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f7b4cdc..5b978e5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2321,7 +2321,7 @@
     field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final String TETHERING_SERVICE = "tethering";
-    field public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+    field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
     field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
     field public static final String VR_SERVICE = "vrmanager";
     field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
@@ -7298,10 +7298,6 @@
 
 package android.net {
 
-  public class DnsResolverServiceManager {
-    method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public static android.os.IBinder getService(@NonNull android.content.Context);
-  }
-
   public class EthernetManager {
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
   }
@@ -7861,7 +7857,7 @@
     method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
     method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
     method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
-    method public boolean registerCountryCodeChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangeListener);
+    method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
     method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
     method public void setOnServiceDeadCallback(@NonNull Runnable);
     method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -7874,7 +7870,7 @@
     method public boolean tearDownClientInterface(@NonNull String);
     method public boolean tearDownInterfaces();
     method public boolean tearDownSoftApInterface(@NonNull String);
-    method public void unregisterCountryCodeChangeListener(@NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangeListener);
+    method public void unregisterCountryCodeChangedListener(@NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
     field public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
     field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1
     field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0
@@ -7885,8 +7881,8 @@
     field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
   }
 
-  public static interface WifiNl80211Manager.CountryCodeChangeListener {
-    method public void onChanged(@NonNull String);
+  public static interface WifiNl80211Manager.CountryCodeChangedListener {
+    method public void onCountryCodeChanged(@NonNull String);
   }
 
   public static class WifiNl80211Manager.OemSecurityType {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a77bf32..e449728 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1575,6 +1575,10 @@
     method public static void setServiceForTest(@Nullable android.os.IBinder);
   }
 
+  public class NetworkWatchlistManager {
+    method @Nullable public byte[] getWatchlistConfigHash();
+  }
+
   public class TrafficStats {
     method public static long getLoopbackRxBytes();
     method public static long getLoopbackRxPackets();
@@ -2010,6 +2014,7 @@
 
   public final class PermissionManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
     method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
   }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index df9530f..1cb46b1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6475,7 +6475,7 @@
                     historicalDiscreteAccesses.add(other.mDiscreteAccesses.get(i++));
                 }
             }
-            mDiscreteAccesses = historicalDiscreteAccesses;
+            mDiscreteAccesses = deduplicateDiscreteEvents(historicalDiscreteAccesses);
         }
 
         private void increaseAccessCount(@UidState int uidState, @OpFlags int flags,
@@ -6996,7 +6996,7 @@
             }
             result.add(entry);
         }
-        return result;
+        return deduplicateDiscreteEvents(result);
     }
 
     /**
@@ -9819,4 +9819,52 @@
             }
         }
     }
+
+    private static List<AttributedOpEntry> deduplicateDiscreteEvents(List<AttributedOpEntry> list) {
+        int n = list.size();
+        int i = 0;
+        for (int j = 0, k = 0; j < n; i++, j = k) {
+            long currentAccessTime = list.get(j).getLastAccessTime(OP_FLAGS_ALL);
+            k = j + 1;
+            while(k < n && list.get(k).getLastAccessTime(OP_FLAGS_ALL) == currentAccessTime) {
+                k++;
+            }
+            list.set(i, mergeAttributedOpEntries(list.subList(j, k)));
+        }
+        for (; i < n; i++) {
+            list.remove(list.size() - 1);
+        }
+        return list;
+    }
+
+    private static AttributedOpEntry mergeAttributedOpEntries(List<AttributedOpEntry> opEntries) {
+        if (opEntries.size() == 1) {
+            return opEntries.get(0);
+        }
+        LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
+        LongSparseArray<AppOpsManager.NoteOpEvent> rejectEvents = new LongSparseArray<>();
+        int opCount = opEntries.size();
+        for (int i = 0; i < opCount; i++) {
+            AttributedOpEntry a = opEntries.get(i);
+            ArraySet<Long> keys = a.collectKeys();
+            final int keyCount = keys.size();
+            for (int k = 0; k < keyCount; k++) {
+                final long key = keys.valueAt(k);
+
+                final int uidState = extractUidStateFromKey(key);
+                final int flags = extractFlagsFromKey(key);
+
+                NoteOpEvent access = a.getLastAccessEvent(uidState, uidState, flags);
+                NoteOpEvent reject = a.getLastRejectEvent(uidState, uidState, flags);
+
+                if (access != null) {
+                    accessEvents.append(key, access);
+                }
+                if (reject != null) {
+                    rejectEvents.append(key, reject);
+                }
+            }
+        }
+        return new AttributedOpEntry(opEntries.get(0).mOp, false, accessEvents, rejectEvents);
+    }
 }
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index e83557c..4f7c684 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -190,4 +190,18 @@
      * Called from SystemUI when it shows the AoD UI.
      */
     oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
+
+    /**
+     * Called from SystemUI when the device is waking up.
+     *
+     * @hide
+     */
+    oneway void notifyWakingUp(int x, int y, in Bundle extras);
+
+    /**
+     * Called from SystemUI when the device is going to sleep.
+     *
+     * @hide
+     */
+    void notifyGoingToSleep(int x, int y, in Bundle extras);
 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 6a71c92..8d332ab 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -190,6 +190,30 @@
     public static final String COMMAND_DROP = "android.home.drop";
 
     /**
+     * Command for {@link #sendWallpaperCommand}: reported by System UI when the device is waking
+     * up. The x and y arguments are a location (possibly very roughly) corresponding to the action
+     * that caused the device to wake up. For example, if the power button was pressed, this will be
+     * the location on the screen nearest the power button.
+     *
+     * If the location is unknown or not applicable, x and y will be -1.
+     *
+     * @hide
+     */
+    public static final String COMMAND_WAKING_UP = "android.wallpaper.wakingup";
+
+    /**
+     * Command for {@link #sendWallpaperCommand}: reported by System UI when the device is going to
+     * sleep. The x and y arguments are a location (possibly very roughly) corresponding to the
+     * action that caused the device to go to sleep. For example, if the power button was pressed,
+     * this will be the location on the screen nearest the power button.
+     *
+     * If the location is unknown or not applicable, x and y will be -1.
+     *
+     * @hide
+     */
+    public static final String COMMAND_GOING_TO_SLEEP = "android.wallpaper.goingtosleep";
+
+    /**
      * Command for {@link #sendWallpaperCommand}: reported when the wallpaper that was already
      * set is re-applied by the user.
      * @hide
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index a72877e..fe81df0 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -320,6 +320,15 @@
     }
 
     /**
+     * Set the host's interaction handler.
+     *
+     * @hide
+     */
+    public void setInteractionHandler(InteractionHandler interactionHandler) {
+        mInteractionHandler = interactionHandler;
+    }
+
+    /**
      * Gets a list of all the appWidgetIds that are bound to the current host
      */
     public int[] getAppWidgetIds() {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 318913f..a88c9ed 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4705,10 +4705,9 @@
      * @hide
      * @see #getSystemService(String)
      */
-    // TODO(b/176208267): change it back to translation before S release.
     @SystemApi
     @SuppressLint("ServiceName")
-    public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+    public static final String TRANSLATION_MANAGER_SERVICE = "translation";
 
     /**
      * Official published name of the translation service which supports ui translation function.
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 98a20f7..52ee4de 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -47,7 +47,7 @@
     public static final int PARTITION_PRODUCT = 4;
     public static final int PARTITION_SYSTEM_EXT = 5;
 
-    @IntDef(flag = true, prefix = { "PARTITION_" }, value = {
+    @IntDef(prefix = { "PARTITION_" }, value = {
         PARTITION_SYSTEM,
         PARTITION_VENDOR,
         PARTITION_ODM,
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
index 4ec2e73..3a4aae1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -40,7 +40,7 @@
     public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
 
     /** Maximum amount of attributions per package */
-    private static final int MAX_NUM_ATTRIBUTIONS = 10000;
+    private static final int MAX_NUM_ATTRIBUTIONS = 1000;
 
     /** Tag of the attribution */
     public final @NonNull String tag;
@@ -100,7 +100,7 @@
 
 
 
-    // Code below generated by codegen v1.0.23.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -215,8 +215,8 @@
     };
 
     @DataClass.Generated(
-            time = 1618351459610L,
-            codegenVersion = "1.0.23",
+            time = 1607463855175L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
             inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
     @Deprecated
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4ee5383..f03da7c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -718,7 +718,7 @@
         public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
                 @NonNull EditorInfo editorInfo, boolean restarting,
                 @NonNull IBinder startInputToken) {
-            mPrivOps.reportStartInput(startInputToken);
+            mPrivOps.reportStartInputAsync(startInputToken);
 
             if (restarting) {
                 restartInput(inputConnection, editorInfo);
diff --git a/core/java/android/net/DnsResolverServiceManager.java b/core/java/android/net/DnsResolverServiceManager.java
deleted file mode 100644
index 1597322..0000000
--- a/core/java/android/net/DnsResolverServiceManager.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open 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.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.ServiceManager;
-
-import java.util.Objects;
-
-/**
- * Provides a way to obtain the DnsResolver binder objects.
- *
- * @hide
- */
-@SystemApi
-public class DnsResolverServiceManager {
-    /**
-     * Name to retrieve a {@link android.net.IDnsResolver} IBinder.
-     */
-    private static final String DNS_RESOLVER_SERVICE = "dnsresolver";
-
-    private DnsResolverServiceManager() {}
-
-    /**
-     * Get an {@link IBinder} representing the DnsResolver stable AIDL interface
-     *
-     * @param context the context for permission check.
-     * @return {@link android.net.IDnsResolver} IBinder.
-     */
-    @NonNull
-    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
-    public static IBinder getService(@NonNull final Context context) {
-        Objects.requireNonNull(context);
-        context.enforceCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-                "DnsResolverServiceManager");
-        try {
-            return ServiceManager.getServiceOrThrow(DNS_RESOLVER_SERVICE);
-        } catch (ServiceManager.ServiceNotFoundException e) {
-            // Catch ServiceManager#ServiceNotFoundException and rethrow IllegalStateException
-            // because ServiceManager#ServiceNotFoundException is @hide so that it can't be listed
-            // on the system api. Thus, rethrow IllegalStateException if dns resolver service cannot
-            // be found.
-            throw new IllegalStateException("Cannot find dns resolver service.");
-        }
-    }
-}
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 8f6510e..da01dcb 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -31,6 +32,7 @@
  * Class that manage network watchlist in system.
  * @hide
  */
+@TestApi
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 @SystemService(Context.NETWORK_WATCHLIST_SERVICE)
 public class NetworkWatchlistManager {
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 16d041a..d026e95 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -34,6 +34,9 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Java proxy for a native IBinder object.
@@ -262,27 +265,45 @@
                 Log.e(Binder.TAG, "RemoteException while disabling app freezer");
             }
 
-            for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
-                BinderProxy bp = weakRef.get();
-                String key;
-                if (bp == null) {
-                    key = "<cleared weak-ref>";
-                } else {
-                    try {
-                        key = bp.getInterfaceDescriptor();
-                        if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
-                            key = "<proxy to dead node>";
+            // We run the dump on a separate thread, because there are known cases where
+            // a process overrides getInterfaceDescriptor() and somehow blocks on it, causing
+            // the calling thread (usually AMS) to hit the watchdog.
+            // Do the dumping on a separate thread instead, and give up after a while.
+            ExecutorService executorService = Executors.newSingleThreadExecutor();
+            executorService.submit(() -> {
+                for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
+                    BinderProxy bp = weakRef.get();
+                    String key;
+                    if (bp == null) {
+                        key = "<cleared weak-ref>";
+                    } else {
+                        try {
+                            key = bp.getInterfaceDescriptor();
+                            if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
+                                key = "<proxy to dead node>";
+                            }
+                        } catch (Throwable t) {
+                            key = "<exception during getDescriptor>";
                         }
-                    } catch (Throwable t) {
-                        key = "<exception during getDescriptor>";
+                    }
+                    Integer i = counts.get(key);
+                    if (i == null) {
+                        counts.put(key, 1);
+                    } else {
+                        counts.put(key, i + 1);
                     }
                 }
-                Integer i = counts.get(key);
-                if (i == null) {
-                    counts.put(key, 1);
-                } else {
-                    counts.put(key, i + 1);
+            });
+
+            try {
+                executorService.shutdown();
+                boolean dumpDone = executorService.awaitTermination(20, TimeUnit.SECONDS);
+                if (!dumpDone) {
+                    Log.e(Binder.TAG, "Failed to complete binder proxy dump,"
+                            + " dumping what we have so far.");
                 }
+            } catch (InterruptedException e) {
+                // Ignore
             }
             try {
                 ActivityManager.getService().enableAppFreezer(true);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 1a40f06..17c90d6 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -886,6 +886,24 @@
     }
 
     /**
+     * @param micMuted whether to consider the microphone muted when retrieving audio ops
+     * @return A list of permission groups currently or recently used by all apps by all users in
+     * the current profile group.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
+    public List<PermGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
+        // Lazily initialize the usage helper
+        if (mUsageHelper == null) {
+            mUsageHelper = new PermissionUsageHelper(mContext);
+        }
+        return mUsageHelper.getOpUsageData(micMuted);
+    }
+
+    /**
      * Determine if a package should be shown in indicators. Only a select few roles, and the
      * system app itself, are hidden. These values are updated at most every 15 seconds.
      * @hide
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d89c29a..87fb611 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
+import android.graphics.BLASTBufferQueue;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
@@ -208,8 +209,8 @@
         int mCurHeight;
         float mZoom = 0f;
         int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-        int mWindowPrivateFlags =
-                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+        int mWindowPrivateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS
+                | WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
         int mCurWindowFlags = mWindowFlags;
         int mCurWindowPrivateFlags = mWindowPrivateFlags;
         Rect mPreviewSurfacePosition;
@@ -253,6 +254,7 @@
         private int mDisplayState;
 
         SurfaceControl mSurfaceControl = new SurfaceControl();
+        BLASTBufferQueue mBlastBufferQueue;
 
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
@@ -974,7 +976,14 @@
                             View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
                             mInsetsState, mTempControls, mSurfaceSize);
                     if (mSurfaceControl.isValid()) {
-                        mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
+                        Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
+                                mSurfaceSize.y, mFormat);
+                        // If blastSurface == null that means it hasn't changed since the last
+                        // time we called. In this situation, avoid calling transferFrom as we
+                        // would then inc the generation ID and cause EGL resources to be recreated.
+                        if (blastSurface != null) {
+                            mSurfaceHolder.mSurface.transferFrom(blastSurface);
+                        }
                     }
                     if (!mLastSurfaceSize.equals(mSurfaceSize)) {
                         mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
@@ -1455,13 +1464,12 @@
                 return;
             }
             Surface surface = mSurfaceHolder.getSurface();
-            boolean widthIsLarger =
-                    mSurfaceControl.getWidth() > mSurfaceControl.getHeight();
-            int smaller = widthIsLarger ? mSurfaceControl.getWidth()
-                    : mSurfaceControl.getHeight();
+            boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
+            int smaller = widthIsLarger ? mSurfaceSize.x
+                    : mSurfaceSize.y;
             float ratio = (float) MIN_BITMAP_SCREENSHOT_WIDTH / (float) smaller;
-            int width = (int) (ratio * mSurfaceControl.getWidth());
-            int height = (int) (ratio * mSurfaceControl.getHeight());
+            int width = (int) (ratio * mSurfaceSize.x);
+            int height = (int) (ratio * mSurfaceSize.y);
             if (width <= 0 || height <= 0) {
                 Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height);
                 return;
@@ -1842,6 +1850,21 @@
             public void onDisplayAdded(int displayId) {
             }
         };
+
+        private Surface getOrCreateBLASTSurface(int width, int height, int format) {
+            Surface ret = null;
+            if (mBlastBufferQueue == null) {
+                mBlastBufferQueue = new BLASTBufferQueue("Wallpaper", mSurfaceControl, width,
+                        height, format);
+                // We only return the Surface the first time, as otherwise
+                // it hasn't changed and there is no need to update.
+                ret = mBlastBufferQueue.createSurface();
+            } else {
+                mBlastBufferQueue.update(mSurfaceControl, width, height, format);
+            }
+
+            return ret;
+        }
     }
 
     private boolean isValid(RectF area) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 21f75d4..2c81e89 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -35,6 +35,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -188,6 +189,10 @@
             IBinder displayToken, int mode);
     private static native void nativeReparent(long transactionObj, long nativeObject,
             long newParentNativeObject);
+    private static native void nativeSetBuffer(long transactionObj, long nativeObject,
+            GraphicBuffer buffer);
+    private static native void nativeSetColorSpace(long transactionObj, long nativeObject,
+            int colorSpace);
 
     private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
 
@@ -3362,6 +3367,31 @@
             return this;
         }
 
+        /**
+         * Set a buffer for a SurfaceControl. This can only be used for SurfaceControls that were
+         * created as type {@link #FX_SURFACE_BLAST}
+         *
+         * @hide
+         */
+        public Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+            checkPreconditions(sc);
+            nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer);
+            return this;
+        }
+
+        /**
+         * Set the color space for the SurfaceControl. The supported color spaces are SRGB
+         * and Display P3, other color spaces will be treated as SRGB. This can only be used for
+         * SurfaceControls that were created as type {@link #FX_SURFACE_BLAST}
+         *
+         * @hide
+         */
+        public Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) {
+            checkPreconditions(sc);
+            nativeSetColorSpace(mNativeObject, sc.mNativeObject, colorSpace.getId());
+            return this;
+        }
+
          /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 76eb882..be8e519 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9309,11 +9309,11 @@
      * Handles an inbound request for scroll capture from the system. A search will be
      * dispatched through the view tree to locate scrolling content.
      * <p>
-     * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)}
-     * will follow.
+     * A call to
+     * {@link IScrollCaptureResponseListener#onScrollCaptureResponse} will follow.
      *
      * @param listener to receive responses
-     * @see ScrollCaptureTargetSelector
+     * @see ScrollCaptureSearchResults
      */
     public void handleScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) {
         ScrollCaptureSearchResults results =
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 616910a..d6292ca 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -581,7 +581,7 @@
      */
     public void reportPerceptible(IBinder windowToken, boolean perceptible) {
         try {
-            mService.reportPerceptible(windowToken, perceptible);
+            mService.reportPerceptibleAsync(windowToken, perceptible);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 20e520e..4365966 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -30,7 +30,7 @@
  */
 oneway interface IInputMethodPrivilegedOperations {
     void setImeWindowStatusAsync(int vis, int backDisposition);
-    void reportStartInput(in IBinder startInputToken, in IVoidResultCallback resultCallback);
+    void reportStartInputAsync(in IBinder startInputToken);
     void createInputContentUriToken(in Uri contentUri, in String packageName,
             in IIInputContentUriTokenResultCallback resultCallback);
     void reportFullscreenMode(boolean fullscreen, in IVoidResultCallback resultCallback);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 1000914..555488d 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -123,21 +123,18 @@
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder,
-     * IVoidResultCallback)}.
+     * Calls {@link IInputMethodPrivilegedOperations#reportStartInputAsync(IBinder)}.
      *
      * @param startInputToken {@link IBinder} token to distinguish startInput session
      */
     @AnyThread
-    public void reportStartInput(IBinder startInputToken) {
+    public void reportStartInputAsync(IBinder startInputToken) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
-            final Completable.Void value = Completable.createVoid();
-            ops.reportStartInput(startInputToken, ResultCallbacks.of(value));
-            Completable.getResult(value);
+            ops.reportStartInputAsync(startInputToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index fd13c26b..93cd4e9 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -85,7 +85,7 @@
     oneway void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
             in float[] matrixValues, in IVoidResultCallback resultCallback);
 
-    oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
+    oneway void reportPerceptibleAsync(in IBinder windowToken, boolean perceptible);
     /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
     oneway void removeImeSurface(in IVoidResultCallback resultCallback);
     /** Remove the IME surface. Requires passing the currently focused window. */
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ffba628..4194acb 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -30,6 +30,7 @@
 #include <android/hardware/display/IDeviceProductInfoConstants.h>
 #include <android/os/IInputConstants.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_graphics_GraphicBuffer.h>
 #include <android_runtime/android_hardware_HardwareBuffer.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_view_SurfaceSession.h>
@@ -253,6 +254,15 @@
     }
 }
 
+constexpr ui::Dataspace fromNamedColorSpaceValueToDataspace(const jint colorSpace) {
+    switch (colorSpace) {
+        case JNamedColorSpace::DISPLAY_P3:
+            return ui::Dataspace::DISPLAY_P3;
+        default:
+            return ui::Dataspace::V0_SRGB;
+    }
+}
+
 constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
     switch (colorMode) {
         case ui::ColorMode::DISPLAY_P3:
@@ -553,6 +563,23 @@
     transaction->setGeometry(ctrl, source, dst, orientation);
 }
 
+static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+                            jobject bufferObject) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    sp<GraphicBuffer> buffer(
+            android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, bufferObject));
+    transaction->setBuffer(ctrl, buffer);
+}
+
+static void nativeSetColorSpace(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+                                jint colorSpace) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    ui::Dataspace dataspace = fromNamedColorSpaceValueToDataspace(colorSpace);
+    transaction->setDataspace(ctrl, dataspace);
+}
+
 static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj,
                                  jlong nativeObject, jobjectArray regions, jint regionsLength) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1877,6 +1904,10 @@
             (void*)nativeGetDisplayedContentSample },
     {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
             (void*)nativeSetGeometry },
+    {"nativeSetBuffer", "(JJLandroid/graphics/GraphicBuffer;)V",
+            (void*)nativeSetBuffer },
+    {"nativeSetColorSpace", "(JJI)V",
+            (void*)nativeSetColorSpace },
     {"nativeSyncInputWindows", "(J)V",
             (void*)nativeSyncInputWindows },
     {"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z",
diff --git a/core/proto/android/internal/OWNERS b/core/proto/android/internal/OWNERS
new file mode 100644
index 0000000..24e24c2
--- /dev/null
+++ b/core/proto/android/internal/OWNERS
@@ -0,0 +1,2 @@
+# Binder
+per-file binder_latency.proto = file:/core/java/com/android/internal/os/BINDER_OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index c393d68..8225afc 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -244,7 +244,8 @@
                         PendingIntent.getActivity(
                                 context, 0, new Intent("action1"), FLAG_IMMUTABLE)))
                 .addAction(new RemoteAction(icon1, "title2", "desc2",
-                          PendingIntent.getActivity(context, 0, new Intent("action2"), 0)))
+                        PendingIntent.getActivity(context, 0, new Intent("action2"),
+                                FLAG_IMMUTABLE)))
                 .setEntityType(TextClassifier.TYPE_EMAIL, 0.5f)
                 .setEntityType(TextClassifier.TYPE_PHONE, 0.4f)
                 .build();
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 6c03ddc..e9e58c6 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -226,10 +226,12 @@
      */
     public void drawRipple(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint,
-            CanvasProperty<Float> progress, RuntimeShader shader) {
+            CanvasProperty<Float> progress, CanvasProperty<Float> turbulencePhase,
+            RuntimeShader shader) {
         nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
                 radius.getNativeContainer(), paint.getNativeContainer(),
-                progress.getNativeContainer(), shader.getNativeShaderBuilder());
+                progress.getNativeContainer(), turbulencePhase.getNativeContainer(),
+                shader.getNativeShaderBuilder());
     }
 
     /**
@@ -290,7 +292,7 @@
             long propCy, long propRadius, long propPaint);
     @CriticalNative
     private static native void nDrawRipple(long renderer, long propCx, long propCy, long propRadius,
-            long propPaint, long propProgress, long runtimeEffect);
+            long propPaint, long propProgress, long turbulencePhase, long runtimeEffect);
     @CriticalNative
     private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
             long propRight, long propBottom, long propRx, long propRy, long propPaint);
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index c317831..1cd4cf1 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -40,6 +40,8 @@
     private static final String TAG = "RippleAnimationSession";
     private static final int ENTER_ANIM_DURATION = 450;
     private static final int EXIT_ANIM_DURATION = 300;
+    private static final long NOISE_ANIMATION_DURATION = 7000;
+    private static final long MAX_NOISE_PHASE = NOISE_ANIMATION_DURATION / 120;
     private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
     private static final Interpolator FAST_OUT_SLOW_IN =
             new PathInterpolator(0.4f, 0f, 0.2f, 1f);
@@ -49,7 +51,7 @@
     private Runnable mOnUpdate;
     private long mStartTime;
     private boolean mForceSoftware;
-    private boolean mAnimateSparkle;
+    private Animator mLoopAnimation;
 
     RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
             boolean forceSoftware) {
@@ -88,16 +90,6 @@
         return this;
     }
 
-    public boolean shouldAnimateSparkle() {
-        return mAnimateSparkle && ValueAnimator.getDurationScale() > 0;
-    }
-
-    public float getSparklePhase() {
-        final long now = AnimationUtils.currentAnimationTimeMillis();
-        final long elapsed = now - mStartTime;
-        return  (float) elapsed / 800;
-    }
-
     private boolean isHwAccelerated(Canvas canvas) {
         return canvas.isHardwareAccelerated() && !mForceSoftware;
     }
@@ -114,7 +106,7 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
-                mAnimateSparkle = false;
+                if (mLoopAnimation != null) mLoopAnimation.cancel();
                 Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
                 if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
             }
@@ -148,7 +140,7 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
-                mAnimateSparkle = false;
+                if (mLoopAnimation != null) mLoopAnimation.cancel();
                 Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
                 if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
             }
@@ -167,24 +159,42 @@
         RenderNodeAnimator expand =
                 new RenderNodeAnimator(props.getProgress(), .5f);
         expand.setTarget(canvas);
-        startAnimation(expand);
+        RenderNodeAnimator loop = new RenderNodeAnimator(props.getNoisePhase(), MAX_NOISE_PHASE);
+        loop.setTarget(canvas);
+        startAnimation(expand, loop);
     }
 
-    private void startAnimation(Animator expand) {
+    private void startAnimation(Animator expand, Animator loop) {
         expand.setDuration(ENTER_ANIM_DURATION);
         expand.addListener(new AnimatorListener(this));
         expand.setInterpolator(FAST_OUT_SLOW_IN);
         expand.start();
-        mAnimateSparkle = true;
+        loop.setDuration(NOISE_ANIMATION_DURATION);
+        loop.addListener(new AnimatorListener(this) {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mLoopAnimation = null;
+            }
+        });
+        loop.setInterpolator(LINEAR_INTERPOLATOR);
+        loop.start();
+        if (mLoopAnimation != null) mLoopAnimation.cancel();
+        mLoopAnimation = loop;
     }
 
     private void enterSoftware() {
         ValueAnimator expand = ValueAnimator.ofFloat(0f, 0.5f);
         expand.addUpdateListener(updatedAnimation -> {
             notifyUpdate();
-            mProperties.getShader().setProgress((Float) expand.getAnimatedValue());
+            mProperties.getShader().setProgress((float) expand.getAnimatedValue());
         });
-        startAnimation(expand);
+        ValueAnimator loop = ValueAnimator.ofFloat(0f, MAX_NOISE_PHASE);
+        loop.addUpdateListener(updatedAnimation -> {
+            notifyUpdate();
+            mProperties.getShader().setNoisePhase((float) loop.getAnimatedValue());
+        });
+        startAnimation(expand, loop);
     }
 
     @NonNull AnimationProperties<Float, Paint> getProperties() {
@@ -198,6 +208,7 @@
                     CanvasProperty.createFloat(mProperties.getX()),
                     CanvasProperty.createFloat(mProperties.getY()),
                     CanvasProperty.createFloat(mProperties.getMaxRadius()),
+                    CanvasProperty.createFloat(mProperties.getNoisePhase()),
                     CanvasProperty.createPaint(mProperties.getPaint()),
                     CanvasProperty.createFloat(mProperties.getProgress()),
                     mProperties.getShader());
@@ -236,16 +247,18 @@
     static class AnimationProperties<FloatType, PaintType> {
         private final FloatType mProgress;
         private final FloatType mMaxRadius;
+        private final FloatType mNoisePhase;
         private final PaintType mPaint;
         private final RippleShader mShader;
         private FloatType mX;
         private FloatType mY;
 
-        AnimationProperties(FloatType x, FloatType y, FloatType maxRadius,
+        AnimationProperties(FloatType x, FloatType y, FloatType maxRadius, FloatType noisePhase,
                 PaintType paint, FloatType progress, RippleShader shader) {
             mY = y;
             mX = x;
             mMaxRadius = maxRadius;
+            mNoisePhase = noisePhase;
             mPaint = paint;
             mShader = shader;
             mProgress = progress;
@@ -279,5 +292,9 @@
         RippleShader getShader() {
             return mShader;
         }
+
+        FloatType getNoisePhase() {
+            return mNoisePhase;
+        }
     }
 }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 0865332..c972a24 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -858,15 +858,6 @@
         }
         for (int i = 0; i < mRunningAnimations.size(); i++) {
             RippleAnimationSession s = mRunningAnimations.get(i);
-            if (s.shouldAnimateSparkle()) {
-                final float phase = s.getSparklePhase();
-                if (useCanvasProps) {
-                    s.getCanvasProperties().getShader().setNoisePhase(phase);
-                } else {
-                    s.getProperties().getShader().setNoisePhase(phase);
-                }
-                invalidateSelf();
-            }
             if (useCanvasProps) {
                 RippleAnimationSession.AnimationProperties<CanvasProperty<Float>,
                         CanvasProperty<Paint>>
@@ -883,7 +874,7 @@
                     yProp = p.getY();
                 }
                 can.drawRipple(xProp, yProp, p.getMaxRadius(), p.getPaint(),
-                        p.getProgress(), p.getShader());
+                        p.getProgress(), p.getNoisePhase(), p.getShader());
             } else {
                 RippleAnimationSession.AnimationProperties<Float, Paint> p =
                         s.getProperties();
@@ -953,7 +944,7 @@
         shader.setRadius(radius);
         shader.setProgress(.0f);
         properties = new RippleAnimationSession.AnimationProperties<>(
-                cx, cy, radius, p, 0f, shader);
+                cx, cy, radius, 0f, p, 0f, shader);
         if (mMaskShader == null) {
             shader.setShader(null);
         } else {
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 4608d02..c1c6afc 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -167,6 +167,9 @@
         final float turbulencePhase = (float) ((mProgress + mNoisePhase * 0.333f) * 5f * Math.PI);
         setUniform("in_turbulencePhase", turbulencePhase);
 
+        //
+        // Keep in sync with: frameworks/base/libs/hwui/pipeline/skia/AnimatedDrawables.h
+        //
         final float scale = 1.5f;
         setUniform("in_tCircle1", new float[]{
                 (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.cos(scale * 0.55))),
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index f3baad7..602cd5d 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -43,10 +43,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
-    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+    <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
-    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
-    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+    <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
+    <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index fed3ea9..a17f543 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -62,10 +62,10 @@
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابک‌ها"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابک‌ها در هرزمانی"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابک‌ها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابک‌ها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
-    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌ها اخیر و حبابک‌ها ردشده اینجا ظاهر خواهند شد"</string>
+    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌های اخیر و حبابک‌های ردشده اینجا ظاهر خواهند شد"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index a969386..b2c0055 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -67,7 +67,7 @@
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
-    <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+    <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
     <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
     <string name="got_it" msgid="4428750913636945527">"ठीक है"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 8ca54e0..ef98a9c 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,6 +19,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
-    <string name="pip_close" msgid="9135220303720555525">"‏סגור PIP"</string>
+    <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 530d40a..0c64c76 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -62,7 +62,7 @@
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
     <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 882ac37..dfa364a 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -30,7 +30,7 @@
     <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
-    <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+    <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
     <string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
     <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 6698a01..a138fee 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -40,7 +40,7 @@
     <integer name="long_press_dock_anim_duration">250</integer>
 
     <!-- Animation duration for translating of one handed when trigger / dismiss. -->
-    <integer name="config_one_handed_translate_animation_duration">300</integer>
+    <integer name="config_one_handed_translate_animation_duration">800</integer>
 
     <!-- One handed mode default offset % of display size -->
     <fraction name="config_one_handed_offset">40%</fraction>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 125e322..25dd3ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -22,8 +22,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
-import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
+import android.view.animation.BaseInterpolator;
 import android.window.WindowContainerToken;
 
 import androidx.annotation.VisibleForTesting;
@@ -54,7 +53,7 @@
     public @interface TransitionDirection {
     }
 
-    private final Interpolator mOvershootInterpolator;
+    private final OneHandedInterpolator mInterpolator;
     private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final HashMap<WindowContainerToken, OneHandedTransitionAnimator> mAnimatorMap =
             new HashMap<>();
@@ -64,7 +63,7 @@
      */
     public OneHandedAnimationController(Context context) {
         mSurfaceTransactionHelper = new OneHandedSurfaceTransactionHelper(context);
-        mOvershootInterpolator = new OvershootInterpolator();
+        mInterpolator = new OneHandedInterpolator();
     }
 
     @SuppressWarnings("unchecked")
@@ -102,7 +101,7 @@
     OneHandedTransitionAnimator setupOneHandedTransitionAnimator(
             OneHandedTransitionAnimator animator) {
         animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
-        animator.setInterpolator(mOvershootInterpolator);
+        animator.setInterpolator(mInterpolator);
         animator.setFloatValues(FRACTION_START, FRACTION_END);
         return animator;
     }
@@ -112,6 +111,8 @@
      *
      * @param <T> Type of property to animate, either offset (float)
      */
+    // TODO: Refactoring to use SpringAnimation and DynamicAnimation instead of using ValueAnimator
+    //  to implement One-Handed transition animation. (b/185129031)
     public abstract static class OneHandedTransitionAnimator<T> extends ValueAnimator implements
             ValueAnimator.AnimatorUpdateListener,
             ValueAnimator.AnimatorListener {
@@ -297,4 +298,15 @@
             };
         }
     }
+
+    /**
+     * An Interpolator for One-Handed transition animation.
+     */
+    public class OneHandedInterpolator extends BaseInterpolator {
+        @Override
+        public float getInterpolation(float input) {
+            return (float) (Math.pow(2, -10 * input) * Math.sin(((input - 4.0f) / 4.0f)
+                    * (2.0f * Math.PI) / 4.0f) + 1);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 19098fd..0a86ad8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -343,7 +343,6 @@
                     mOneHandedAccessibilityUtil.getOneHandedStartDescription());
             mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
             mTimeoutHandler.resetTimer();
-
             mOneHandedUiEventLogger.writeEvent(
                     OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
         }
@@ -351,12 +350,7 @@
 
     @VisibleForTesting
     void stopOneHanded() {
-        if (mDisplayAreaOrganizer.isInOneHanded()) {
-            mOneHandedAccessibilityUtil.announcementForScreenReader(
-                    mOneHandedAccessibilityUtil.getOneHandedStopDescription());
-            mDisplayAreaOrganizer.scheduleOffset(0, 0);
-            mTimeoutHandler.removeTimer();
-        }
+        stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
     }
 
     private void stopOneHanded(int uiEvent) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index e50bde2..6494f89 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -23,7 +23,7 @@
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_TITLE
+import com.android.server.wm.flicker.HOME_WINDOW_TITLE
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -62,7 +62,7 @@
     override val ignoredWindows: List<String>
         get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME,
             splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *LAUNCHER_TITLE)
+            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
 
     @FlakyTest(bugId = 169271943)
     @Test
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 4c4a152..3056e97 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -820,10 +820,11 @@
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint,
                             uirenderer::CanvasPropertyPrimitive* progress,
+                            uirenderer::CanvasPropertyPrimitive* turbulencePhase,
                             const SkRuntimeShaderBuilder& effectBuilder) {
     sk_sp<uirenderer::skiapipeline::AnimatedRipple> drawable(
             new uirenderer::skiapipeline::AnimatedRipple(x, y, radius, paint, progress,
-                                                         effectBuilder));
+                                                         turbulencePhase, effectBuilder));
     mCanvas->drawDrawable(drawable.get());
 }
 
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index e0a0be5..995f00c 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -153,6 +153,7 @@
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint,
                             uirenderer::CanvasPropertyPrimitive* progress,
+                            uirenderer::CanvasPropertyPrimitive* turbulencePhase,
                             const SkRuntimeShaderBuilder& effectBuilder) override;
 
     virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 855cd0d..173f394 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -152,32 +152,66 @@
     sp<uirenderer::CanvasPropertyPrimitive> radius;
     sp<uirenderer::CanvasPropertyPaint> paint;
     sp<uirenderer::CanvasPropertyPrimitive> progress;
+    sp<uirenderer::CanvasPropertyPrimitive> turbulencePhase;
     sk_sp<SkRuntimeEffect> effect;
 
+    const float PI = 3.1415926535897932384626;
+    const float PI_ROTATE_RIGHT = PI * 0.0078125;
+    const float PI_ROTATE_LEFT = PI * -0.0078125;
+    const float SCALE = 1.5;
+    const float CIRCLE_X_1 = 0.01 * cos(SCALE * 0.55);
+    const float CIRCLE_Y_1 = 0.01 * sin(SCALE * 0.55);
+    const float CIRCLE_X_2 = -0.0066 * cos(SCALE * 0.45);
+    const float CIRCLE_Y_2 = -0.0066 * sin(SCALE * 0.45);
+    const float CIRCLE_X_3 = -0.0066 * cos(SCALE * 0.35);
+    const float CIRCLE_Y_3 = -0.0066 * sin(SCALE * 0.35);
+
     void draw(SkCanvas* canvas) const {
         SkRuntimeShaderBuilder runtimeEffectBuilder(effect);
 
-        SkRuntimeShaderBuilder::BuilderUniform center = runtimeEffectBuilder.uniform("in_origin");
-        if (center.fVar != nullptr) {
-            center = SkV2{x->value, y->value};
-        }
+        setUniform2f(runtimeEffectBuilder, "in_origin", x->value, y->value);
+        setUniform(runtimeEffectBuilder, "in_radius", radius);
+        setUniform(runtimeEffectBuilder, "in_progress", progress);
+        setUniform(runtimeEffectBuilder, "in_turbulencePhase", turbulencePhase);
 
-        SkRuntimeShaderBuilder::BuilderUniform radiusU =
-                runtimeEffectBuilder.uniform("in_radius");
-        if (radiusU.fVar != nullptr) {
-            radiusU = radius->value;
-        }
-
-        SkRuntimeShaderBuilder::BuilderUniform progressU =
-                runtimeEffectBuilder.uniform("in_progress");
-        if (progressU.fVar != nullptr) {
-            progressU = progress->value;
-        }
+        //
+        // Keep in sync with:
+        // frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java
+        //
+        const float turbulence = turbulencePhase->value;
+        setUniform2f(runtimeEffectBuilder, "in_tCircle1", SCALE * 0.5 + (turbulence * CIRCLE_X_1),
+                     SCALE * 0.5 + (turbulence * CIRCLE_Y_1));
+        setUniform2f(runtimeEffectBuilder, "in_tCircle2", SCALE * 0.2 + (turbulence * CIRCLE_X_2),
+                     SCALE * 0.2 + (turbulence * CIRCLE_Y_2));
+        setUniform2f(runtimeEffectBuilder, "in_tCircle3", SCALE + (turbulence * CIRCLE_X_3),
+                     SCALE + (turbulence * CIRCLE_Y_3));
+        const float rotation1 = turbulence * PI_ROTATE_RIGHT + 1.7 * PI;
+        setUniform2f(runtimeEffectBuilder, "in_tRotation1", cos(rotation1), sin(rotation1));
+        const float rotation2 = turbulence * PI_ROTATE_LEFT + 2 * PI;
+        setUniform2f(runtimeEffectBuilder, "in_tRotation2", cos(rotation2), sin(rotation2));
+        const float rotation3 = turbulence * PI_ROTATE_RIGHT + 2.75 * PI;
+        setUniform2f(runtimeEffectBuilder, "in_tRotation3", cos(rotation3), sin(rotation3));
 
         SkPaint paintMod = paint->value;
         paintMod.setShader(runtimeEffectBuilder.makeShader(nullptr, false));
         canvas->drawCircle(x->value, y->value, radius->value, paintMod);
     }
+
+    void setUniform(SkRuntimeShaderBuilder& effect, std::string name,
+                    sp<uirenderer::CanvasPropertyPrimitive> property) const {
+        SkRuntimeShaderBuilder::BuilderUniform uniform = effect.uniform(name.c_str());
+        if (uniform.fVar != nullptr) {
+            uniform = property->value;
+        }
+    }
+
+    void setUniform2f(SkRuntimeShaderBuilder effect, std::string name, float a, float b) const {
+        SkRuntimeShaderBuilder::BuilderUniform uniform = effect.uniform(name.c_str());
+        if (uniform.fVar != nullptr) {
+            uniform = SkV2{a, b};
+        }
+    }
+
     ASSERT_DRAWABLE()
 };
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index c1feb76..837b055 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -146,6 +146,7 @@
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint,
                             uirenderer::CanvasPropertyPrimitive* progress,
+                            uirenderer::CanvasPropertyPrimitive* turbulencePhase,
                             const SkRuntimeShaderBuilder& effectBuilder) = 0;
 
     virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0;
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index 855d56e..eb5a88a 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -141,20 +141,22 @@
     canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
 }
 
-static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
-                                                           jlong xPropPtr, jlong yPropPtr,
-                                                           jlong radiusPropPtr, jlong paintPropPtr,
-                                                           jlong progressPropPtr,
-                                                           jlong builderPtr) {
+static void android_view_DisplayListCanvas_drawRippleProps(
+        CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong xPropPtr, jlong yPropPtr,
+        jlong radiusPropPtr, jlong paintPropPtr, jlong progressPropPtr, jlong turbulencePhasePtr,
+        jlong builderPtr) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
     CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
     CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
+    CanvasPropertyPrimitive* turbulencePhaseProp =
+            reinterpret_cast<CanvasPropertyPrimitive*>(turbulencePhasePtr);
     CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
     CanvasPropertyPrimitive* progressProp =
             reinterpret_cast<CanvasPropertyPrimitive*>(progressPropPtr);
     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(builderPtr);
-    canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, *builder);
+    canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, turbulencePhaseProp,
+                       *builder);
 }
 
 static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) {
@@ -169,19 +171,22 @@
 const char* const kClassPathName = "android/graphics/RecordingCanvas";
 
 static JNINativeMethod gMethods[] = {
-    // ------------ @CriticalNative --------------
-    { "nCreateDisplayListCanvas", "(JII)J",     (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
-    { "nResetDisplayListCanvas",  "(JJII)V",    (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
-    { "nGetMaximumTextureWidth",  "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureSize },
-    { "nGetMaximumTextureHeight", "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureSize },
-    { "nEnableZ",                 "(JZ)V",      (void*) android_view_DisplayListCanvas_enableZ },
-    { "nFinishRecording",         "(JJ)V",      (void*) android_view_DisplayListCanvas_finishRecording },
-    { "nDrawRenderNode",          "(JJ)V",      (void*) android_view_DisplayListCanvas_drawRenderNode },
-    { "nDrawTextureLayer",        "(JJ)V",      (void*) android_view_DisplayListCanvas_drawTextureLayer },
-    { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
-    { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
-    { "nDrawWebViewFunctor",      "(JI)V",      (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
-    { "nDrawRipple",              "(JJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRippleProps },
+        // ------------ @CriticalNative --------------
+        {"nCreateDisplayListCanvas", "(JII)J",
+         (void*)android_view_DisplayListCanvas_createDisplayListCanvas},
+        {"nResetDisplayListCanvas", "(JJII)V",
+         (void*)android_view_DisplayListCanvas_resetDisplayListCanvas},
+        {"nGetMaximumTextureWidth", "()I", (void*)android_view_DisplayListCanvas_getMaxTextureSize},
+        {"nGetMaximumTextureHeight", "()I",
+         (void*)android_view_DisplayListCanvas_getMaxTextureSize},
+        {"nEnableZ", "(JZ)V", (void*)android_view_DisplayListCanvas_enableZ},
+        {"nFinishRecording", "(JJ)V", (void*)android_view_DisplayListCanvas_finishRecording},
+        {"nDrawRenderNode", "(JJ)V", (void*)android_view_DisplayListCanvas_drawRenderNode},
+        {"nDrawTextureLayer", "(JJ)V", (void*)android_view_DisplayListCanvas_drawTextureLayer},
+        {"nDrawCircle", "(JJJJJ)V", (void*)android_view_DisplayListCanvas_drawCircleProps},
+        {"nDrawRoundRect", "(JJJJJJJJ)V", (void*)android_view_DisplayListCanvas_drawRoundRectProps},
+        {"nDrawWebViewFunctor", "(JI)V", (void*)android_view_DisplayListCanvas_drawWebViewFunctor},
+        {"nDrawRipple", "(JJJJJJJJ)V", (void*)android_view_DisplayListCanvas_drawRippleProps},
 };
 
 int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index 7859145..7d65be1 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -19,6 +19,7 @@
 #include <SkCanvas.h>
 #include <SkDrawable.h>
 #include <SkRuntimeEffect.h>
+#include <math.h>
 #include <utils/RefBase.h>
 #include "CanvasProperty.h"
 
@@ -61,12 +62,14 @@
                    uirenderer::CanvasPropertyPrimitive* radius,
                    uirenderer::CanvasPropertyPaint* paint,
                    uirenderer::CanvasPropertyPrimitive* progress,
+                   uirenderer::CanvasPropertyPrimitive* turbulencePhase,
                    const SkRuntimeShaderBuilder& effectBuilder)
             : mX(x)
             , mY(y)
             , mRadius(radius)
             , mPaint(paint)
             , mProgress(progress)
+            , mTurbulencePhase(turbulencePhase)
             , mRuntimeEffectBuilder(effectBuilder) {}
 
 protected:
@@ -77,22 +80,28 @@
         return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
     }
     virtual void onDraw(SkCanvas* canvas) override {
-        SkRuntimeShaderBuilder::BuilderUniform center = mRuntimeEffectBuilder.uniform("in_origin");
-        if (center.fVar != nullptr) {
-            center = SkV2{mX->value, mY->value};
-        }
+        setUniform2f("in_origin", mX->value, mY->value);
+        setUniform("in_radius", mRadius);
+        setUniform("in_progress", mProgress);
+        setUniform("in_turbulencePhase", mTurbulencePhase);
 
-        SkRuntimeShaderBuilder::BuilderUniform radiusU =
-                mRuntimeEffectBuilder.uniform("in_radius");
-        if (radiusU.fVar != nullptr) {
-            radiusU = mRadius->value;
-        }
-
-        SkRuntimeShaderBuilder::BuilderUniform progressU =
-                mRuntimeEffectBuilder.uniform("in_progress");
-        if (progressU.fVar != nullptr) {
-            progressU = mProgress->value;
-        }
+        //
+        // Keep in sync with:
+        // frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java
+        //
+        const float turbulencePhase = mTurbulencePhase->value;
+        setUniform2f("in_tCircle1", SCALE * 0.5 + (turbulencePhase * CIRCLE_X_1),
+                     SCALE * 0.5 + (turbulencePhase * CIRCLE_Y_1));
+        setUniform2f("in_tCircle2", SCALE * 0.2 + (turbulencePhase * CIRCLE_X_2),
+                     SCALE * 0.2 + (turbulencePhase * CIRCLE_Y_2));
+        setUniform2f("in_tCircle3", SCALE + (turbulencePhase * CIRCLE_X_3),
+                     SCALE + (turbulencePhase * CIRCLE_Y_3));
+        const float rotation1 = turbulencePhase * PI_ROTATE_RIGHT + 1.7 * PI;
+        setUniform2f("in_tRotation1", cos(rotation1), sin(rotation1));
+        const float rotation2 = turbulencePhase * PI_ROTATE_LEFT + 2 * PI;
+        setUniform2f("in_tRotation2", cos(rotation2), sin(rotation2));
+        const float rotation3 = turbulencePhase * PI_ROTATE_RIGHT + 2.75 * PI;
+        setUniform2f("in_tRotation3", cos(rotation3), sin(rotation3));
 
         SkPaint paint = mPaint->value;
         paint.setShader(mRuntimeEffectBuilder.makeShader(nullptr, false));
@@ -105,7 +114,35 @@
     sp<uirenderer::CanvasPropertyPrimitive> mRadius;
     sp<uirenderer::CanvasPropertyPaint> mPaint;
     sp<uirenderer::CanvasPropertyPrimitive> mProgress;
+    sp<uirenderer::CanvasPropertyPrimitive> mTurbulencePhase;
     SkRuntimeShaderBuilder mRuntimeEffectBuilder;
+
+    const float PI = 3.1415926535897932384626;
+    const float PI_ROTATE_RIGHT = PI * 0.0078125;
+    const float PI_ROTATE_LEFT = PI * -0.0078125;
+    const float SCALE = 1.5;
+    const float CIRCLE_X_1 = 0.01 * cos(SCALE * 0.55);
+    const float CIRCLE_Y_1 = 0.01 * sin(SCALE * 0.55);
+    const float CIRCLE_X_2 = -0.0066 * cos(SCALE * 0.45);
+    const float CIRCLE_Y_2 = -0.0066 * sin(SCALE * 0.45);
+    const float CIRCLE_X_3 = -0.0066 * cos(SCALE * 0.35);
+    const float CIRCLE_Y_3 = -0.0066 * sin(SCALE * 0.35);
+
+    virtual void setUniform(std::string name, sp<uirenderer::CanvasPropertyPrimitive> property) {
+        SkRuntimeShaderBuilder::BuilderUniform uniform =
+                mRuntimeEffectBuilder.uniform(name.c_str());
+        if (uniform.fVar != nullptr) {
+            uniform = property->value;
+        }
+    }
+
+    virtual void setUniform2f(std::string name, float a, float b) {
+        SkRuntimeShaderBuilder::BuilderUniform uniform =
+                mRuntimeEffectBuilder.uniform(name.c_str());
+        if (uniform.fVar != nullptr) {
+            uniform = SkV2{a, b};
+        }
+    }
 };
 
 class AnimatedCircle : public SkDrawable {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 82814de..9e73f04 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -60,12 +60,11 @@
     // Add the marker annotation to allow HWUI to determine where the current
     // clip/transformation should be applied
     SkVector vector = rect.getSimpleRadii();
-    const int dataSize = 2;
-    float data[dataSize];
+    float data[2];
     data[0] = vector.x();
     data[1] = vector.y();
     mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
-                             SkData::MakeWithCopy(data, dataSize));
+                             SkData::MakeWithCopy(data, 2 * sizeof(float)));
 
     // Clear the current rect within the layer itself
     SkPaint paint = SkPaint();
@@ -115,9 +114,10 @@
                                      uirenderer::CanvasPropertyPrimitive* radius,
                                      uirenderer::CanvasPropertyPaint* paint,
                                      uirenderer::CanvasPropertyPrimitive* progress,
+                                     uirenderer::CanvasPropertyPrimitive* turbulencePhase,
                                      const SkRuntimeShaderBuilder& effectBuilder) {
     drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress,
-                                                                effectBuilder));
+                                                                turbulencePhase, effectBuilder));
 }
 
 void SkiaRecordingCanvas::enableZ(bool enableZ) {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 06f2a27..4deb3b9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -75,6 +75,7 @@
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint,
                             uirenderer::CanvasPropertyPrimitive* progress,
+                            uirenderer::CanvasPropertyPrimitive* turbulencePhase,
                             const SkRuntimeShaderBuilder& effectBuilder) override;
 
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 6bfbb0d..a6e4c4c 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -22,7 +22,7 @@
 
 void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
     if (HOLE_PUNCH_ANNOTATION == key) {
-        auto* rectParams = static_cast<const float*>(value->data());
+        auto* rectParams = reinterpret_cast<const float*>(value->data());
         float radiusX = rectParams[0];
         float radiusY = rectParams[1];
         SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 83cc263..07d04aa 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -22,6 +22,6 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
     <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
-    <string name="consent_yes" msgid="8344487259618762872">"مجاز"</string>
-    <string name="consent_no" msgid="2640796915611404382">"مجاز نیست"</string>
+    <string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
+    <string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
 </resources>
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index c62402d..bb93af9 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -100,6 +100,7 @@
         "//frameworks/base",
 
         // Tests using hidden APIs
+        "//cts/tests/netlegacy22.api",
         "//external/sl4a:__subpackages__",
         "//frameworks/base/tests/net:__subpackages__",
         "//frameworks/libs/net/common/testutils",
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 2194575..3ca7475 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -277,7 +277,7 @@
     method @NonNull public int[] getAdministratorUids();
     method @Nullable public static String getCapabilityCarrierName(int);
     method @Nullable public String getSsid();
-    method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
+    method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
     method @NonNull public int[] getTransportTypes();
     method public boolean isPrivateDnsBroken();
     method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
@@ -308,7 +308,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
-    method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+    method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
     method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
   }
 
@@ -338,7 +338,7 @@
 
   public static class NetworkRequest.Builder {
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
-    method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+    method @NonNull public android.net.NetworkRequest.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
   }
 
   public final class NetworkScore implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
index 92a792b..a2e218d 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
@@ -68,5 +68,11 @@
                     return cm.startOrGetTestNetworkManager();
                 }
         );
+
+        SystemServiceRegistry.registerContextAwareService(
+                DnsResolverServiceManager.DNS_RESOLVER_SERVICE,
+                DnsResolverServiceManager.class,
+                (context, serviceBinder) -> new DnsResolverServiceManager(serviceBinder)
+        );
     }
 }
diff --git a/packages/Connectivity/framework/src/android/net/DnsResolverServiceManager.java b/packages/Connectivity/framework/src/android/net/DnsResolverServiceManager.java
new file mode 100644
index 0000000..79009e8
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/DnsResolverServiceManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open 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.IBinder;
+
+/**
+ * Provides a way to obtain the DnsResolver binder objects.
+ *
+ * @hide
+ */
+public class DnsResolverServiceManager {
+    /** Service name for the DNS resolver. Keep in sync with DnsResolverService.h */
+    public static final String DNS_RESOLVER_SERVICE = "dnsresolver";
+
+    private final IBinder mResolver;
+
+    DnsResolverServiceManager(IBinder resolver) {
+        mResolver = resolver;
+    }
+
+    /**
+     * Get an {@link IBinder} representing the DnsResolver stable AIDL interface
+     *
+     * @return {@link android.net.IDnsResolver} IBinder.
+     */
+    @NonNull
+    public IBinder getService() {
+        return mResolver;
+    }
+}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c4277c3..775c88f 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -1749,7 +1749,7 @@
         combineSSIDs(nc);
         combineRequestor(nc);
         combineAdministratorUids(nc);
-        combineSubIds(nc);
+        combineSubscriptionIds(nc);
     }
 
     /**
@@ -1771,7 +1771,7 @@
                 && (onlyImmutable || satisfiedByUids(nc))
                 && (onlyImmutable || satisfiedBySSID(nc))
                 && (onlyImmutable || satisfiedByRequestor(nc))
-                && (onlyImmutable || satisfiedBySubIds(nc)));
+                && (onlyImmutable || satisfiedBySubscriptionIds(nc)));
     }
 
     /**
@@ -1868,7 +1868,7 @@
                 && equalsPrivateDnsBroken(that)
                 && equalsRequestor(that)
                 && equalsAdministratorUids(that)
-                && equalsSubIds(that);
+                && equalsSubscriptionIds(that);
     }
 
     @Override
@@ -2346,7 +2346,7 @@
      * @hide
      */
     @NonNull
-    public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+    public NetworkCapabilities setSubscriptionIds(@NonNull Set<Integer> subIds) {
         mSubIds = new ArraySet(Objects.requireNonNull(subIds));
         return this;
     }
@@ -2362,14 +2362,14 @@
      */
     @NonNull
     @SystemApi
-    public Set<Integer> getSubIds() {
+    public Set<Integer> getSubscriptionIds() {
         return new ArraySet<>(mSubIds);
     }
 
     /**
      * Tests if the subscription ID set of this network is the same as that of the passed one.
      */
-    private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+    private boolean equalsSubscriptionIds(@NonNull NetworkCapabilities nc) {
         return Objects.equals(mSubIds, nc.mSubIds);
     }
 
@@ -2378,7 +2378,7 @@
      * If specified in the request, the passed one need to have at least one subId and at least
      * one of them needs to be in the request set.
      */
-    private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+    private boolean satisfiedBySubscriptionIds(@NonNull NetworkCapabilities nc) {
         if (mSubIds.isEmpty()) return true;
         if (nc.mSubIds.isEmpty()) return false;
         for (final Integer subId : nc.mSubIds) {
@@ -2395,7 +2395,7 @@
      * <p>If both subscription IDs are not equal, they belong to different subscription
      * (or no subscription). In this case, it would not make sense to add them together.
      */
-    private void combineSubIds(@NonNull NetworkCapabilities nc) {
+    private void combineSubscriptionIds(@NonNull NetworkCapabilities nc) {
         if (!Objects.equals(mSubIds, nc.mSubIds)) {
             throw new IllegalStateException("Can't combine two subscription ID sets");
         }
@@ -2737,8 +2737,8 @@
          */
         @NonNull
         @SystemApi
-        public Builder setSubIds(@NonNull final Set<Integer> subIds) {
-            mCaps.setSubIds(subIds);
+        public Builder setSubscriptionIds(@NonNull final Set<Integer> subIds) {
+            mCaps.setSubscriptionIds(subIds);
             return this;
         }
 
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 78e1011..90aac0e 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -508,8 +508,8 @@
          */
         @NonNull
         @SystemApi
-        public Builder setSubIds(@NonNull Set<Integer> subIds) {
-            mNetworkCapabilities.setSubIds(subIds);
+        public Builder setSubscriptionIds(@NonNull Set<Integer> subIds) {
+            mNetworkCapabilities.setSubscriptionIds(subIds);
             return this;
         }
     }
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
index a24456e..f57061a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
@@ -18,5 +18,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"વ્યવસ્થાપકે ચાલુ કરેલ"</string>
-    <string name="disabled_by_admin" msgid="4023569940620832713">"વ્યવસ્થાપકે બંધ કરેલ"</string>
+    <string name="disabled_by_admin" msgid="4023569940620832713">"વ્યવસ્થાપકે બંધ કરેલું"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
index 199a2d6..bddf43c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
@@ -18,5 +18,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Attivata dall\'amministratore"</string>
-    <string name="disabled_by_admin" msgid="4023569940620832713">"Disattivata dall\'amministratore"</string>
+    <string name="disabled_by_admin" msgid="4023569940620832713">"Opzione disattivata dall\'amministratore"</string>
 </resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a834784..173e959 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -762,7 +762,7 @@
                   android:showForAllUsers="true"
                   android:finishOnTaskLaunch="true"
                   android:launchMode="singleInstance"
-                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+                  android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
         </activity>
 
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 3363f8e..03d844a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -31,14 +31,16 @@
     companion object {
         const val ANIMATION_DURATION = 500L
         const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L
-        const val ANIMATION_DURATION_FADE_IN_WINDOW = 216L
-        const val ANIMATION_DELAY_FADE_IN_WINDOW = 166L
+        const val ANIMATION_DURATION_FADE_IN_WINDOW = 217L
+        const val ANIMATION_DELAY_FADE_IN_WINDOW = 167L
         private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
         private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
         private const val ANIMATION_DELAY_NAV_FADE_IN =
                 ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
         private const val LAUNCH_TIMEOUT = 1000L
 
+        private val CONTENT_FADE_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f)
+        private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
         private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
         private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
 
@@ -418,12 +420,12 @@
                 val contentAlphaProgress = getProgress(linearProgress, 0,
                         ANIMATION_DURATION_FADE_OUT_CONTENT)
                 state.contentAlpha =
-                        1 - Interpolators.ALPHA_OUT.getInterpolation(contentAlphaProgress)
+                        1 - CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(contentAlphaProgress)
 
                 val backgroundAlphaProgress = getProgress(linearProgress,
                         ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
                 state.backgroundAlpha =
-                        1 - Interpolators.ALPHA_IN.getInterpolation(backgroundAlphaProgress)
+                        1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(backgroundAlphaProgress)
 
                 applyStateToWindow(window, state)
                 navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 01ec447..3da4521 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -9,6 +9,7 @@
 import android.graphics.Rect
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.InsetDrawable
 import android.graphics.drawable.LayerDrawable
 import android.view.GhostView
 import android.view.View
@@ -186,6 +187,10 @@
                 return drawable
             }
 
+            if (drawable is InsetDrawable) {
+                return drawable.drawable?.let { findGradientDrawable(it) }
+            }
+
             if (drawable is LayerDrawable) {
                 for (i in 0 until drawable.numberOfLayers) {
                     val maybeGradient = drawable.getDrawable(i)
@@ -255,6 +260,11 @@
         }
 
         private fun setXfermode(background: Drawable, mode: PorterDuffXfermode?) {
+            if (background is InsetDrawable) {
+                background.drawable?.let { setXfermode(it, mode) }
+                return
+            }
+
             if (background !is LayerDrawable) {
                 background.setXfermode(mode)
                 return
@@ -323,6 +333,11 @@
                 return
             }
 
+            if (drawable is InsetDrawable) {
+                drawable.drawable?.let { applyBackgroundRadii(it, radii) }
+                return
+            }
+
             if (drawable !is LayerDrawable) {
                 return
             }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 00bea8d..47a373e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -60,6 +60,8 @@
      */
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
     void startActivity(Intent intent, boolean dismissShade);
+    void startActivity(Intent intent, boolean dismissShade,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
     void postStartActivityDismissingKeyguard(Intent intent, int delay);
diff --git a/packages/SystemUI/res/drawable/ic_brightness.xml b/packages/SystemUI/res/drawable/ic_brightness.xml
index f443332..842af26 100644
--- a/packages/SystemUI/res/drawable/ic_brightness.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness.xml
@@ -1,29 +1,23 @@
 <!--
-Copyright (C) 2020 The Android Open Source Project
+  ~ 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.
+  -->
 
-   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="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:pathData="M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z"
-    />
-
-    <path
-        android:pathData=" M20,8.69 V4h-4.69L12,0.69L8.69,4H4v4.69L0.69,12L4,15.31V20h4.69L12,23.31L15.31,20H20v-4.69L23.31,12L20,8.69z M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S14.76,7 12,7z"
-        android:fillColor="#FFFFFF" />
-</vector>
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Levels in drawables go from 0 to 10000 -->
+    <!-- "One third" of the range per icon -->
+    <item android:maxLevel="3333" android:drawable="@drawable/ic_brightness_low" />
+    <item android:maxLevel="6666" android:drawable="@drawable/ic_brightness_medium" />
+    <item android:maxLevel="10000" android:drawable="@drawable/ic_brightness_full" />
+</level-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml b/packages/SystemUI/res/drawable/ic_brightness_full.xml
similarity index 86%
rename from packages/SystemUI/res/drawable/ic_brightness_thumb.xml
rename to packages/SystemUI/res/drawable/ic_brightness_full.xml
index d721988..f443332 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_full.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2017 The Android Open Source Project
+Copyright (C) 2020 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -21,9 +21,9 @@
 
     <path
         android:pathData="M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z"
-        android:fillColor="?android:attr/colorBackgroundFloating" />
+    />
 
     <path
         android:pathData=" M20,8.69 V4h-4.69L12,0.69L8.69,4H4v4.69L0.69,12L4,15.31V20h4.69L12,23.31L15.31,20H20v-4.69L23.31,12L20,8.69z M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S14.76,7 12,7z"
-        android:fillColor="?android:attr/colorControlActivated" />
+        android:fillColor="#FFFFFF" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_low.xml b/packages/SystemUI/res/drawable/ic_brightness_low.xml
new file mode 100644
index 0000000..b463556
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_brightness_low.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,9c1.65,0 3,1.35 3,3s-1.35,3 -3,3 -3,-1.35 -3,-3 1.35,-3 3,-3m0,-2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_medium.xml b/packages/SystemUI/res/drawable/ic_brightness_medium.xml
new file mode 100644
index 0000000..80acc4d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_brightness_medium.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,17c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5v10z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
index 6022206..77b9871 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
@@ -23,13 +23,18 @@
         <item android:id="@android:id/mask">
             <shape android:shape="rectangle">
                 <solid android:color="@android:color/white"/>
+                <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+            </shape>
+        </item>
+        <item>
+            <shape android:shape="rectangle">
+                <solid android:color="?attr/underSurfaceColor"/>
                 <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
             </shape>
         </item>
         <item>
             <shape android:shape="rectangle">
-            <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
-                <solid android:color="@android:color/transparent"/>
+                <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
                 <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
             </shape>
         </item>
diff --git a/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
new file mode 100644
index 0000000..4e9d380
--- /dev/null
+++ b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open 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.
+-->
+
+<!-- dot drawable for ongoing system privacy events -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid
+        android:color="@color/privacy_circle"/>
+    <size
+        android:width="6dp"
+        android:height="6dp"
+        />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_empty.xml b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
index 7d9cbb9..4236493 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_empty.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:theme="@android:style/Theme.DeviceDefault.DayNight"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -22,57 +22,49 @@
     <LinearLayout
         android:background="@drawable/people_space_tile_view_card"
         android:id="@+id/item"
-        android:orientation="vertical"
+        android:gravity="center"
+        android:paddingHorizontal="16dp"
+        android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
-
+        <ImageView
+            android:id="@+id/person_icon"
+            android:layout_marginTop="-2dp"
+            android:layout_marginStart="-2dp"
+            android:layout_width="64dp"
+            android:layout_height="64dp" />
+        <ImageView
+            android:id="@+id/availability"
+            android:layout_marginStart="-2dp"
+            android:layout_width="10dp"
+            android:layout_height="10dp"
+            android:background="@drawable/circle_green_10dp" />
         <LinearLayout
-            android:orientation="horizontal"
-            android:gravity="center"
-            android:layout_gravity="center"
-            android:paddingVertical="2dp"
-            android:paddingHorizontal="8dp"
+            android:orientation="vertical"
+            android:paddingStart="6dp"
+            android:gravity="top"
             android:layout_width="match_parent"
-            android:layout_height="match_parent">
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_width="64dp"
-                android:layout_height="64dp"/>
-            <ImageView
-                android:id="@+id/availability"
-                android:layout_marginStart="-2dp"
-                android:layout_width="10dp"
-                android:layout_height="10dp"
-                android:background="@drawable/circle_green_10dp"/>
-            <LinearLayout
-                android:orientation="vertical"
-                android:paddingStart="6dp"
-                android:gravity="top"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-
-                <TextView
-                    android:id="@+id/name"
-                    android:text="@string/empty_user_name"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                    android:textColor="?android:attr/textColorPrimary"
-                    android:textSize="14sp"
-                    android:maxLines="1"
-                    android:ellipsize="end"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"/>
-
-                <TextView
-                    android:id="@+id/last_interaction"
-                    android:text="@string/empty_status"
-                    android:textColor="?android:attr/textColorSecondary"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                    android:textSize="12sp"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:maxLines="3"
-                    android:ellipsize="end"/>
-            </LinearLayout>
+            android:layout_height="wrap_content">
+            <TextView
+                android:id="@+id/name"
+                android:text="@string/empty_user_name"
+                android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"
+                android:maxLines="1"
+                android:ellipsize="end"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            <TextView
+                android:id="@+id/last_interaction"
+                android:text="@string/empty_status"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                android:textSize="12sp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="3"
+                android:ellipsize="end" />
         </LinearLayout>
     </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index c9e4945..7070660 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -97,7 +97,7 @@
             android:gravity="bottom"
             android:layout_gravity="center_vertical"
             android:orientation="horizontal"
-            android:paddingTop="4dp"
+            android:paddingTop="2dp"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:clipToOutline="true">
diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml
index 34aa8e4..7c28fc1 100644
--- a/packages/SystemUI/res/layout/people_tile_small.xml
+++ b/packages/SystemUI/res/layout/people_tile_small.xml
@@ -21,7 +21,7 @@
     <LinearLayout
         android:id="@+id/item"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
         android:layout_gravity="center"
         android:background="@drawable/people_space_tile_view_card"
         android:orientation="vertical"
@@ -42,12 +42,12 @@
             android:tint="?android:attr/colorAccent"
             android:layout_gravity="center"
             android:layout_width="18dp"
-            android:layout_height="22dp"
-            android:layout_weight="1" />
+            android:layout_height="22dp" />
 
         <TextView
             android:id="@+id/messages_count"
             android:layout_gravity="center"
+            android:gravity="center"
             android:paddingStart="8dp"
             android:paddingEnd="8dp"
             android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
@@ -59,7 +59,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:visibility="gone"
-            android:layout_weight="1"
             />
 
         <TextView
@@ -67,7 +66,6 @@
             android:layout_gravity="center"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
             android:ellipsize="end"
             android:maxLines="1"
             android:paddingHorizontal="4dp"
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index ae3adb8..4fcce24 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -31,8 +31,7 @@
             android:paddingTop="2dp"
             android:paddingStart="16dp"
             android:paddingEnd="12dp"
-            android:layout_marginRight="20dp"
-            android:layout_marginLeft="20dp"
+            android:layout_marginLeft="16dp"
             android:layout_marginTop="5dp"
             android:layout_marginBottom="16dp"
             android:layout_gravity="start|center_vertical"
@@ -52,12 +51,10 @@
             android:layout_gravity="center_vertical">
 
         <ImageButton
-        android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                android:paddingLeft="10dp"
-                android:layout_marginBottom="12dp"
-                android:layout_marginEnd="12dp"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:layout_gravity="center_horizontal|bottom"
+                android:layout_marginBottom="22dp"
                 android:id="@+id/remote_input_send"
                 android:src="@drawable/ic_send"
                 android:contentDescription="@*android:string/ime_action_send"
@@ -69,8 +66,8 @@
                 android:id="@+id/remote_input_progress"
                 android:layout_width="24dp"
                 android:layout_height="24dp"
-                android:layout_marginBottom="12dp"
-                android:layout_gravity="center_vertical"
+                android:layout_marginBottom="34dp"
+                android:layout_gravity="center_horizontal|bottom"
                 android:visibility="invisible"
                 android:indeterminate="true"
                 style="?android:attr/progressBarStyleSmall" />
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index db892d7..04fe918 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -14,6 +14,8 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 -->
+
+<!-- TODO: remove this in favor of requiring top and bottom layouts -->
 <com.android.systemui.RegionInterceptingFrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/rounded_corners_default"
@@ -26,6 +28,25 @@
         android:layout_gravity="left|top"
         android:tint="#ff000000"
         android:src="@drawable/rounded"/>
+
+    <FrameLayout
+        android:id="@+id/privacy_dot_left_container"
+        android:layout_height="@dimen/status_bar_height"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_padding_top"
+        android:layout_marginLeft="8dp"
+        android:layout_gravity="left|top"
+        android:visibility="invisible" >
+        <ImageView
+            android:id="@+id/privacy_dot_left"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center"
+            android:src="@drawable/system_animation_ongoing_dot"
+            android:visibility="visible" />
+    </FrameLayout>
+
+
     <ImageView
         android:id="@+id/right"
         android:layout_width="12dp"
@@ -33,4 +54,22 @@
         android:tint="#ff000000"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded"/>
+    <FrameLayout
+        android:id="@+id/privacy_dot_right_container"
+        android:layout_height="@dimen/status_bar_height"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_padding_top"
+        android:layout_marginRight="8dp"
+        android:layout_gravity="right|top"
+        android:visibility="invisible" >
+        <ImageView
+            android:id="@+id/privacy_dot_right"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center"
+            android:src="@drawable/system_animation_ongoing_dot"
+            android:visibility="visible" />
+
+    </FrameLayout>
+
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
index dde1248..720e47b 100644
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
@@ -26,6 +26,24 @@
         android:layout_gravity="left|bottom"
         android:tint="#ff000000"
         android:src="@drawable/rounded_corner_bottom"/>
+
+    <FrameLayout
+        android:id="@+id/privacy_dot_left_container"
+        android:layout_height="@dimen/status_bar_height"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_padding_top"
+        android:layout_marginLeft="0dp"
+        android:layout_gravity="left|bottom"
+        android:visibility="invisible" >
+        <ImageView
+            android:id="@+id/privacy_dot"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_vertical|right"
+            android:src="@drawable/system_animation_ongoing_dot"
+            android:visibility="visible" />
+    </FrameLayout>
+
     <ImageView
         android:id="@+id/right"
         android:layout_width="12dp"
@@ -33,4 +51,21 @@
         android:tint="#ff000000"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded_corner_bottom"/>
+    <FrameLayout
+        android:id="@+id/privacy_dot_right_container"
+        android:layout_height="@dimen/status_bar_height"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_padding_top"
+        android:layout_marginRight="0dp"
+        android:layout_gravity="right|bottom"
+        android:visibility="invisible" >
+        <ImageView
+            android:id="@+id/privacy_dot"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_vertical|left"
+            android:src="@drawable/system_animation_ongoing_dot"
+            android:visibility="visible" />
+    </FrameLayout>
+
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
index 813c97d..6abe406 100644
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_top.xml
@@ -26,6 +26,24 @@
         android:layout_gravity="left|top"
         android:tint="#ff000000"
         android:src="@drawable/rounded_corner_top"/>
+
+    <FrameLayout
+        android:id="@+id/privacy_dot_left_container"
+        android:layout_height="@*android:dimen/status_bar_height_portrait"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_padding_top"
+        android:layout_marginLeft="0dp"
+        android:layout_gravity="left|top"
+        android:visibility="invisible" >
+        <ImageView
+            android:id="@+id/privacy_dot"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_vertical|right"
+            android:src="@drawable/system_animation_ongoing_dot"
+            android:visibility="visible" />
+    </FrameLayout>
+
     <ImageView
         android:id="@+id/right"
         android:layout_width="12dp"
@@ -33,4 +51,24 @@
         android:tint="#ff000000"
         android:layout_gravity="right|top"
         android:src="@drawable/rounded_corner_top"/>
+
+    <FrameLayout
+        android:id="@+id/privacy_dot_right_container"
+        android:layout_height="@*android:dimen/status_bar_height_portrait"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_padding_top"
+        android:layout_marginRight="0dp"
+        android:layout_gravity="right|top"
+        android:visibility="invisible" >
+        <ImageView
+            android:id="@+id/privacy_dot"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_vertical|left"
+            android:src="@drawable/system_animation_ongoing_dot"
+            android:visibility="visible" />
+
+    </FrameLayout>
+
+
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/system_event_animation_window.xml b/packages/SystemUI/res/layout/system_event_animation_window.xml
new file mode 100644
index 0000000..c92dec9
--- /dev/null
+++ b/packages/SystemUI/res/layout/system_event_animation_window.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_vertical|end"
+    android:paddingTop="@dimen/status_bar_padding_top"
+    android:paddingEnd="8dp"
+    >
+
+    <ImageView
+        android:id="@+id/dot_view"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:layout_gravity="center_vertical|end"
+        android:src="@drawable/system_animation_ongoing_dot"
+        android:visibility="invisible"
+        />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 62ac75e..5d9c909 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1397,6 +1397,7 @@
     <dimen name="max_people_avatar_size_for_large_content">64dp</dimen>
     <dimen name="max_people_avatar_size">108dp</dimen>
     <dimen name="name_text_size_for_small">14sp</dimen>
+    <dimen name="name_text_size_for_medium">14sp</dimen>
     <dimen name="name_text_size_for_large">24sp</dimen>
     <dimen name="content_text_size_for_medium">12sp</dimen>
     <dimen name="content_text_size_for_large">14sp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 983b1bb..62d5a45 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -91,6 +91,13 @@
     }
 
     @Override
+    public void startActivity(Intent intent, boolean dismissShade,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(
+                starter -> starter.get().startActivity(intent, dismissShade, animationController));
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
         mActualStarter.ifPresent(
                 starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade));
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a686fc0..cbfdce5 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -79,6 +79,8 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.NotificationFilter;
@@ -352,6 +354,8 @@
     @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
     @Inject Lazy<NavigationBarOverlayController> mNavbarButtonsControllerLazy;
     @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
+    @Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
+    @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
 
     @Inject
     public Dependency() {
@@ -561,6 +565,10 @@
 
         mProviders.put(NavigationBarOverlayController.class, mNavbarButtonsControllerLazy::get);
 
+        mProviders.put(SystemStatusAnimationScheduler.class,
+                mSystemStatusAnimationSchedulerLazy::get);
+        mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
+
         Dependency.setInstance(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 5fa98bc..d07723e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -88,6 +88,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.qs.SecureSetting;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.settings.SecureSettings;
@@ -126,6 +127,7 @@
     private DisplayManager.DisplayListener mDisplayListener;
     private CameraAvailabilityListener mCameraListener;
     private final UserTracker mUserTracker;
+    private final PrivacyDotViewController mDotViewController;
 
     //TODO: These are piecemeal being updated to Points for now to support non-square rounded
     // corners. for now it is only supposed when reading the intrinsic size from the drawables with
@@ -140,6 +142,11 @@
     protected View[] mOverlays;
     @Nullable
     private DisplayCutoutView[] mCutoutViews;
+    //TODO:
+    View mTopLeftDot;
+    View mTopRightDot;
+    View mBottomLeftDot;
+    View mBottomRightDot;
     private float mDensity;
     private WindowManager mWindowManager;
     private int mRotation;
@@ -147,6 +154,8 @@
     private Handler mHandler;
     private boolean mPendingRotationChange;
     private boolean mIsRoundedCornerMultipleRadius;
+    private int mStatusBarHeightPortrait;
+    private int mStatusBarHeightLandscape;
 
     private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
             new CameraAvailabilityListener.CameraTransitionCallback() {
@@ -205,13 +214,15 @@
             SecureSettings secureSettings,
             BroadcastDispatcher broadcastDispatcher,
             TunerService tunerService,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            PrivacyDotViewController dotViewController) {
         super(context);
         mMainHandler = handler;
         mSecureSettings = secureSettings;
         mBroadcastDispatcher = broadcastDispatcher;
         mTunerService = tunerService;
         mUserTracker = userTracker;
+        mDotViewController = dotViewController;
     }
 
     @Override
@@ -222,6 +233,7 @@
         }
         mHandler = startHandlerThread();
         mHandler.post(this::startOnScreenDecorationsThread);
+        mDotViewController.setUiExecutor(mHandler::post);
     }
 
     @VisibleForTesting
@@ -286,6 +298,7 @@
 
     private void setupDecorations() {
         if (hasRoundedCorners() || shouldDrawCutout()) {
+            updateStatusBarHeight();
             final DisplayCutout cutout = getCutout();
             final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
             int rotatedPos;
@@ -298,6 +311,10 @@
                     removeOverlay(i);
                 }
             }
+            // Overlays have been created, send the dots to the controller
+            //TODO: need a better way to do this
+            mDotViewController.initialize(
+                    mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
         } else {
             removeAllOverlays();
         }
@@ -431,14 +448,21 @@
     private View overlayForPosition(@BoundsPosition int pos) {
         switch (pos) {
             case BOUNDS_POSITION_TOP:
-                return LayoutInflater.from(mContext)
+            case BOUNDS_POSITION_LEFT:
+                View top = LayoutInflater.from(mContext)
                         .inflate(R.layout.rounded_corners_top, null);
+                mTopLeftDot = top.findViewById(R.id.privacy_dot_left_container);
+                mTopRightDot = top.findViewById(R.id.privacy_dot_right_container);
+                return top;
             case BOUNDS_POSITION_BOTTOM:
-                return LayoutInflater.from(mContext)
+            case BOUNDS_POSITION_RIGHT:
+                View bottom =  LayoutInflater.from(mContext)
                         .inflate(R.layout.rounded_corners_bottom, null);
+                mBottomLeftDot = bottom.findViewById(R.id.privacy_dot_left_container);
+                mBottomRightDot = bottom.findViewById(R.id.privacy_dot_right_container);
+                return bottom;
             default:
-                return LayoutInflater.from(mContext)
-                        .inflate(R.layout.rounded_corners, null);
+                throw new IllegalArgumentException("Unknown bounds position");
         }
     }
 
@@ -575,6 +599,11 @@
             View child;
             for (int j = 0; j < size; j++) {
                 child = ((ViewGroup) mOverlays[i]).getChildAt(j);
+                if (child.getId() == R.id.privacy_dot_left_container
+                        || child.getId() == R.id.privacy_dot_right_container) {
+                    // Exclude privacy dot from color inversion (for now?)
+                    continue;
+                }
                 if (child instanceof ImageView) {
                     ((ImageView) child).setImageTintList(tintList);
                 } else if (child instanceof DisplayCutoutView) {
@@ -611,10 +640,15 @@
         Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
                 "must call on " + mHandler.getLooper().getThread()
                         + ", but was " + Thread.currentThread());
+
+        int newRotation = mContext.getDisplay().getRotation();
+        if (mRotation != newRotation) {
+            mDotViewController.updateRotation(newRotation);
+        }
+
         if (mPendingRotationChange) {
             return;
         }
-        int newRotation = mContext.getDisplay().getRotation();
         if (newRotation != mRotation) {
             mRotation = newRotation;
 
@@ -630,6 +664,14 @@
         }
     }
 
+    private void updateStatusBarHeight() {
+        mStatusBarHeightLandscape = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height_landscape);
+        mStatusBarHeightPortrait = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height_portrait);
+        mDotViewController.setStatusBarHeights(mStatusBarHeightPortrait, mStatusBarHeightLandscape);
+    }
+
     private void updateRoundedCornerRadii() {
         // We should eventually move to just using the intrinsic size of the drawables since
         // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
index 2661d89..b544599 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
@@ -60,17 +60,31 @@
     /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks.
      *
-     * @return List of active AppOps information
+     * @return List of active AppOps information, without paused elements.
      */
     List<AppOpItem> getActiveAppOps();
 
     /**
+     * Returns a copy of the list containing all the active AppOps that the controller tracks.
+     *
+     * @param showPaused {@code true} to also obtain paused items. {@code false} otherwise.
+     * @return List of active AppOps information
+     */
+    List<AppOpItem> getActiveAppOps(boolean showPaused);
+
+    /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks, for
      * a given user id.
      *
      * @param userId User id to track
+     * @param showPaused {@code true} to also obtain paused items. {@code false} otherwise.
      *
      * @return List of active AppOps information for that user id
      */
-    List<AppOpItem> getActiveAppOpsForUser(int userId);
+    List<AppOpItem> getActiveAppOpsForUser(int userId, boolean showPaused);
+
+    /**
+     * @return whether this controller is considering the microphone as muted.
+     */
+    boolean isMicMuted();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 994401d..534f93e 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -241,9 +241,9 @@
             AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
             if (item == null && active) {
                 item = new AppOpItem(code, uid, packageName, mClock.elapsedRealtime());
-                if (code == AppOpsManager.OP_RECORD_AUDIO) {
+                if (isOpMicrophone(code)) {
                     item.setDisabled(isAnyRecordingPausedLocked(uid));
-                } else if (code == AppOpsManager.OP_CAMERA) {
+                } else if (isOpCamera(code)) {
                     item.setDisabled(mCameraDisabled);
                 }
                 mActiveItems.add(item);
@@ -298,6 +298,11 @@
         return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
     }
 
+    @WorkerThread
+    public List<AppOpItem> getActiveAppOps() {
+        return getActiveAppOps(false);
+    }
+
     /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks.
      *
@@ -306,8 +311,8 @@
      * @return List of active AppOps information
      */
     @WorkerThread
-    public List<AppOpItem> getActiveAppOps() {
-        return getActiveAppOpsForUser(UserHandle.USER_ALL);
+    public List<AppOpItem> getActiveAppOps(boolean showPaused) {
+        return getActiveAppOpsForUser(UserHandle.USER_ALL, showPaused);
     }
 
     /**
@@ -321,7 +326,7 @@
      * @return List of active AppOps information for that user id
      */
     @WorkerThread
-    public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+    public List<AppOpItem> getActiveAppOpsForUser(int userId, boolean showPaused) {
         Assert.isNotMainThread();
         List<AppOpItem> list = new ArrayList<>();
         synchronized (mActiveItems) {
@@ -330,7 +335,8 @@
                 AppOpItem item = mActiveItems.get(i);
                 if ((userId == UserHandle.USER_ALL
                         || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item.getPackageName()) && !item.isDisabled()) {
+                        && isUserVisible(item.getPackageName())
+                        && (showPaused || !item.isDisabled())) {
                     list.add(item);
                 }
             }
@@ -441,9 +447,9 @@
                 AppOpItem item = mActiveItems.get(i);
 
                 boolean paused = false;
-                if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) {
+                if (isOpMicrophone(item.getCode())) {
                     paused = isAnyRecordingPausedLocked(item.getUid());
-                } else if (item.getCode() == AppOpsManager.OP_CAMERA) {
+                } else if (isOpCamera(item.getCode())) {
                     paused = mCameraDisabled;
                 }
 
@@ -502,6 +508,19 @@
         });
     }
 
+    @Override
+    public boolean isMicMuted() {
+        return mMicMuted;
+    }
+
+    private boolean isOpCamera(int op) {
+        return op == AppOpsManager.OP_CAMERA || op == AppOpsManager.OP_PHONE_CALL_CAMERA;
+    }
+
+    private boolean isOpMicrophone(int op) {
+        return op == AppOpsManager.OP_RECORD_AUDIO || op == AppOpsManager.OP_PHONE_CALL_MICROPHONE;
+    }
+
     protected class H extends Handler {
         H(Looper looper) {
             super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index de00d50..1a94473 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -17,10 +17,20 @@
 package com.android.systemui.keyguard;
 
 import android.annotation.IntDef;
+import android.app.IWallpaperManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.Bundle;
 import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.Trace;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Nullable;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 
 import java.io.FileDescriptor;
@@ -31,7 +41,7 @@
 import javax.inject.Inject;
 
 /**
- * Tracks the wakefulness lifecycle.
+ * Tracks the wakefulness lifecycle, including why we're waking or sleeping.
  */
 @SysUISingleton
 public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements
@@ -51,13 +61,30 @@
     public static final int WAKEFULNESS_AWAKE = 2;
     public static final int WAKEFULNESS_GOING_TO_SLEEP = 3;
 
+    private final Context mContext;
+    private final DisplayMetrics mDisplayMetrics;
+    private final IWallpaperManager mWallpaperManagerService;
+
     private int mWakefulness = WAKEFULNESS_ASLEEP;
+
     private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
+
+    @Nullable
+    private Point mLastWakeOriginLocation = null;
+
     private @PowerManager.GoToSleepReason int mLastSleepReason =
             PowerManager.GO_TO_SLEEP_REASON_MIN;
 
+    @Nullable
+    private Point mLastSleepOriginLocation = null;
+
     @Inject
-    public WakefulnessLifecycle() {
+    public WakefulnessLifecycle(
+            Context context,
+            @Nullable IWallpaperManager wallpaperManagerService) {
+        mContext = context;
+        mDisplayMetrics = context.getResources().getDisplayMetrics();
+        mWallpaperManagerService = wallpaperManagerService;
     }
 
     public @Wakefulness int getWakefulness() {
@@ -85,6 +112,15 @@
         }
         setWakefulness(WAKEFULNESS_WAKING);
         mLastWakeReason = pmWakeReason;
+        updateLastWakeOriginLocation();
+
+        try {
+            mWallpaperManagerService.notifyWakingUp(
+                    mLastWakeOriginLocation.x, mLastWakeOriginLocation.y, new Bundle());
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+
         dispatch(Observer::onStartedWakingUp);
     }
 
@@ -102,6 +138,15 @@
         }
         setWakefulness(WAKEFULNESS_GOING_TO_SLEEP);
         mLastSleepReason = pmSleepReason;
+        updateLastSleepOriginLocation();
+
+        try {
+            mWallpaperManagerService.notifyGoingToSleep(
+                    mLastSleepOriginLocation.x, mLastSleepOriginLocation.y, new Bundle());
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+
         dispatch(Observer::onStartedGoingToSleep);
     }
 
@@ -124,6 +169,60 @@
         Trace.traceCounter(Trace.TRACE_TAG_APP, "wakefulness", wakefulness);
     }
 
+    private void updateLastWakeOriginLocation() {
+        mLastWakeOriginLocation = null;
+
+        switch (mLastWakeReason) {
+            case PowerManager.WAKE_REASON_POWER_BUTTON:
+                mLastWakeOriginLocation = getPowerButtonOrigin();
+                break;
+            default:
+                mLastWakeOriginLocation = getDefaultWakeSleepOrigin();
+                break;
+        }
+    }
+
+    private void updateLastSleepOriginLocation() {
+        mLastSleepOriginLocation = null;
+
+        switch (mLastSleepReason) {
+            case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
+                mLastSleepOriginLocation = getPowerButtonOrigin();
+                break;
+            default:
+                mLastSleepOriginLocation = getDefaultWakeSleepOrigin();
+                break;
+        }
+    }
+
+    /**
+     * Returns the point on the screen closest to the physical power button.
+     */
+    private Point getPowerButtonOrigin() {
+        final boolean isPortrait = mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_PORTRAIT;
+
+        if (isPortrait) {
+            return new Point(
+                    mDisplayMetrics.widthPixels,
+                    mContext.getResources().getDimensionPixelSize(
+                            R.dimen.physical_power_button_center_screen_location_y));
+        } else {
+            return new Point(
+                    mContext.getResources().getDimensionPixelSize(
+                            R.dimen.physical_power_button_center_screen_location_y),
+                    mDisplayMetrics.heightPixels);
+        }
+    }
+
+    /**
+     * Returns the point on the screen used as the default origin for wake/sleep events. This is the
+     * middle-bottom of the screen.
+     */
+    private Point getDefaultWakeSleepOrigin() {
+        return new Point(mDisplayMetrics.widthPixels / 2, mDisplayMetrics.heightPixels);
+    }
+
     public interface Observer {
         default void onStartedWakingUp() {}
         default void onFinishedWakingUp() {}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a0b5521..38a6186 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -23,18 +23,14 @@
 import static com.android.systemui.people.PeopleTileViewHelper.getSizeInDp;
 
 import android.app.Activity;
-import android.app.INotificationManager;
-import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Outline;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
-import android.os.ServiceManager;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -43,7 +39,6 @@
 
 import com.android.systemui.R;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -56,19 +51,13 @@
     private static final String TAG = "PeopleSpaceActivity";
     private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
 
-    private IPeopleManager mPeopleManager;
     private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
-    private INotificationManager mNotificationManager;
-    private LauncherApps mLauncherApps;
     private Context mContext;
-    private NotificationEntryManager mNotificationEntryManager;
     private int mAppWidgetId;
 
     @Inject
-    public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager,
-            PeopleSpaceWidgetManager peopleSpaceWidgetManager) {
+    public PeopleSpaceActivity(PeopleSpaceWidgetManager peopleSpaceWidgetManager) {
         super();
-        mNotificationEntryManager = notificationEntryManager;
         mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
 
     }
@@ -77,11 +66,6 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mContext = getApplicationContext();
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        mPeopleManager = IPeopleManager.Stub.asInterface(
-                ServiceManager.getService(Context.PEOPLE_SERVICE));
-        mLauncherApps = mContext.getSystemService(LauncherApps.class);
         mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
                 INVALID_APPWIDGET_ID);
         setResult(RESULT_CANCELED);
@@ -92,10 +76,8 @@
         List<PeopleSpaceTile> priorityTiles = new ArrayList<>();
         List<PeopleSpaceTile> recentTiles = new ArrayList<>();
         try {
-            priorityTiles = PeopleSpaceUtils.getPriorityTiles(mContext, mNotificationManager,
-                    mPeopleManager, mLauncherApps, mNotificationEntryManager);
-            recentTiles = PeopleSpaceUtils.getRecentTiles(mContext, mNotificationManager,
-                    mPeopleManager, mLauncherApps, mNotificationEntryManager);
+            priorityTiles = mPeopleSpaceWidgetManager.getPriorityTiles();
+            recentTiles = mPeopleSpaceWidgetManager.getRecentTiles();
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve conversations", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 0fbe1f7..a160379 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -19,8 +19,6 @@
 import static android.app.Notification.CATEGORY_MISSED_CALL;
 import static android.app.Notification.EXTRA_MESSAGES;
 
-import android.annotation.NonNull;
-import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.people.ConversationChannel;
 import android.app.people.IPeopleManager;
@@ -36,16 +34,13 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.icu.text.MeasureFormat;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.ContactsContract;
-import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
@@ -65,7 +60,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.text.SimpleDateFormat;
-import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -73,7 +67,6 @@
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -85,9 +78,6 @@
     /** Turns on debugging information about People Space. */
     public static final boolean DEBUG = true;
     private static final String TAG = "PeopleSpaceUtils";
-    private static final int DAYS_IN_A_WEEK = 7;
-    private static final int MIN_HOUR = 1;
-    private static final int ONE_DAY = 1;
     public static final String PACKAGE_NAME = "package_name";
     public static final String USER_ID = "user_id";
     public static final String SHORTCUT_ID = "shortcut_id";
@@ -127,58 +117,6 @@
         }
     }
 
-    /** Returns a list of map entries corresponding to user's priority conversations. */
-    @NonNull
-    public static List<PeopleSpaceTile> getPriorityTiles(
-            Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
-            LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
-            throws Exception {
-        List<ConversationChannelWrapper> conversations =
-                notificationManager.getConversations(
-                        false).getList();
-        // Add priority conversations to tiles list.
-        Stream<ShortcutInfo> priorityConversations = conversations.stream()
-                .filter(c -> c.getNotificationChannel() != null
-                        && c.getNotificationChannel().isImportantConversation())
-                .map(c -> c.getShortcutInfo());
-        List<PeopleSpaceTile> priorityTiles = getSortedTiles(peopleManager, launcherApps,
-                priorityConversations);
-        priorityTiles = augmentTilesFromVisibleNotifications(
-                context, priorityTiles, notificationEntryManager);
-        return priorityTiles;
-    }
-
-    /** Returns a list of map entries corresponding to user's recent conversations. */
-    @NonNull
-    public static List<PeopleSpaceTile> getRecentTiles(
-            Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
-            LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
-            throws Exception {
-        if (DEBUG) Log.d(TAG, "Add recent conversations");
-        List<ConversationChannelWrapper> conversations =
-                notificationManager.getConversations(
-                        false).getList();
-        Stream<ShortcutInfo> nonPriorityConversations = conversations.stream()
-                .filter(c -> c.getNotificationChannel() == null
-                        || !c.getNotificationChannel().isImportantConversation())
-                .map(c -> c.getShortcutInfo());
-
-        List<ConversationChannel> recentConversationsList =
-                peopleManager.getRecentConversations().getList();
-        Stream<ShortcutInfo> recentConversations = recentConversationsList
-                .stream()
-                .map(c -> c.getShortcutInfo());
-
-        Stream<ShortcutInfo> mergedStream = Stream.concat(nonPriorityConversations,
-                recentConversations);
-        List<PeopleSpaceTile> recentTiles =
-                getSortedTiles(peopleManager, launcherApps, mergedStream);
-
-        recentTiles = augmentTilesFromVisibleNotifications(
-                context, recentTiles, notificationEntryManager);
-        return recentTiles;
-    }
-
     /** Returns stored widgets for the conversation specified. */
     public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) {
         if (!key.isValid()) {
@@ -255,7 +193,8 @@
         return augmentedTile.get(0);
     }
 
-    static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
+    /** Adds to {@code tiles} any visible notifications. */
+    public static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
             List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
         if (notificationEntryManager == null) {
             Log.w(TAG, "NotificationEntryManager is null");
@@ -356,11 +295,12 @@
     }
 
     /** Returns a list sorted by ascending last interaction time from {@code stream}. */
-    private static List<PeopleSpaceTile> getSortedTiles(IPeopleManager peopleManager,
-            LauncherApps launcherApps,
+    public static List<PeopleSpaceTile> getSortedTiles(IPeopleManager peopleManager,
+            LauncherApps launcherApps, UserManager userManager,
             Stream<ShortcutInfo> stream) {
         return stream
                 .filter(Objects::nonNull)
+                .filter(c -> !userManager.isQuietModeEnabled(c.getUserHandle()))
                 .map(c -> new PeopleSpaceTile.Builder(c, launcherApps).build())
                 .filter(c -> shouldKeepConversation(c))
                 .map(c -> c.toBuilder().setLastInteractionTimestamp(
@@ -426,36 +366,6 @@
         return bitmap;
     }
 
-    /** Returns a readable status describing the {@code lastInteraction}. */
-    public static String getLastInteractionString(Context context, long lastInteraction) {
-        if (lastInteraction == 0L) {
-            Log.e(TAG, "Could not get valid last interaction");
-            return context.getString(R.string.basic_status);
-        }
-        long now = System.currentTimeMillis();
-        Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
-        MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
-                MeasureFormat.FormatWidth.WIDE);
-        if (durationSinceLastInteraction.toHours() < MIN_HOUR) {
-            return context.getString(R.string.timestamp, formatter.formatMeasures(
-                    new Measure(durationSinceLastInteraction.toMinutes(), MeasureUnit.MINUTE)));
-        } else if (durationSinceLastInteraction.toDays() < ONE_DAY) {
-            return context.getString(R.string.timestamp, formatter.formatMeasures(
-                    new Measure(durationSinceLastInteraction.toHours(),
-                            MeasureUnit.HOUR)));
-        } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
-            return context.getString(R.string.timestamp, formatter.formatMeasures(
-                    new Measure(durationSinceLastInteraction.toHours(),
-                            MeasureUnit.DAY)));
-        } else {
-            return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
-                            ? R.string.timestamp : R.string.over_timestamp,
-                    formatter.formatMeasures(
-                            new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
-                                    MeasureUnit.WEEK)));
-        }
-    }
-
     /**
      * Returns whether the {@code conversation} should be kept for display in the People Space.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 51af47d..6b917c5 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -33,7 +33,6 @@
 import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap;
 import static com.android.systemui.people.PeopleSpaceUtils.getUserId;
 
-import android.annotation.ColorInt;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.app.people.ConversationStatus;
@@ -41,27 +40,28 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.icu.text.MeasureFormat;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
 import android.net.Uri;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.IconDrawableFactory;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.people.widget.LaunchConversationActivity;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
 
 import java.text.NumberFormat;
+import java.time.Duration;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
@@ -76,6 +76,10 @@
     public static final boolean DEBUG = true;
     private static final String TAG = "PeopleTileView";
 
+    private static final int DAYS_IN_A_WEEK = 7;
+    private static final int ONE_DAY = 1;
+    private static final int MAX_WEEKS = 2;
+
     public static final int LAYOUT_SMALL = 0;
     public static final int LAYOUT_MEDIUM = 1;
     public static final int LAYOUT_LARGE = 2;
@@ -83,7 +87,9 @@
     private static final int MIN_CONTENT_MAX_LINES = 2;
 
     private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT = 14 + 12 + 4 + 16;
-    private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT = 8 + 4 + 4 + 8;
+    private static final int MIN_MEDIUM_VERTICAL_PADDING = 4;
+    private static final int MAX_MEDIUM_PADDING = 16;
+    private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING = 4 + 4;
     private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8;
     private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4;
 
@@ -96,6 +102,8 @@
 
     public static final String EMPTY_STRING = "";
 
+    private int mMediumVerticalPadding;
+
     private Context mContext;
     private PeopleSpaceTile mTile;
     private float mDensity;
@@ -205,7 +213,8 @@
     private int getContentHeightForLayout(int lineHeight) {
         switch (mLayoutSize) {
             case LAYOUT_MEDIUM:
-                return mHeight - (lineHeight + FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT);
+                return mHeight - (lineHeight + FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING
+                        + mMediumVerticalPadding * 2);
             case LAYOUT_LARGE:
                 return mHeight - (getSizeInDp(
                         R.dimen.max_people_avatar_size_for_large_content) + lineHeight
@@ -224,7 +233,16 @@
         }
         // Small layout used below a certain minimum mWidth with any mHeight.
         if (mWidth >= getSizeInDp(R.dimen.required_width_for_medium)) {
-            if (DEBUG) Log.d(TAG, "Medium view for mWidth: " + mWidth + " mHeight: " + mHeight);
+            int spaceAvailableForPadding =
+                    mHeight - (getSizeInDp(R.dimen.avatar_size_for_medium) + 4 + getLineHeight(
+                            getSizeInDp(R.dimen.name_text_size_for_medium)));
+            if (DEBUG) {
+                Log.d(TAG, "Medium view for mWidth: " + mWidth + " mHeight: " + mHeight
+                        + " with padding space: " + spaceAvailableForPadding);
+            }
+            int maxVerticalPadding = Math.min(Math.floorDiv(spaceAvailableForPadding, 2),
+                    MAX_MEDIUM_PADDING);
+            mMediumVerticalPadding = Math.max(MIN_MEDIUM_VERTICAL_PADDING, maxVerticalPadding);
             return LAYOUT_MEDIUM;
         }
         // Small layout can always handle our minimum mWidth and mHeight for our widget.
@@ -347,11 +365,7 @@
             setMaxLines(views);
             CharSequence content = mTile.getNotificationContent();
             views = setPunctuationRemoteViewsFields(views, content);
-            // TODO(b/184931139): Update to RemoteViews wrapper to set via attribute once available
-            @ColorInt int color = Utils.getColorAttr(mContext,
-                    android.R.attr.textColorPrimary).getDefaultColor();
-            views.setInt(R.id.text_content, "setTextColor", color);
-
+            views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorPrimary);
             views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
             views.setViewVisibility(R.id.image, View.GONE);
             views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
@@ -398,9 +412,7 @@
         views.setViewVisibility(R.id.messages_count, View.GONE);
         setMaxLines(views);
         // Secondary text color for statuses.
-        @ColorInt int secondaryColor = Utils.getColorAttr(mContext,
-                android.R.attr.textColorSecondary).getDefaultColor();
-        views.setInt(R.id.text_content, "setTextColor", secondaryColor);
+        views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
         views.setTextViewText(R.id.text_content, statusText);
 
         Icon statusIcon = status.getIcon();
@@ -541,6 +553,14 @@
             views.setViewVisibility(R.id.text_content, View.VISIBLE);
             views.setViewVisibility(R.id.subtext, View.GONE);
         }
+
+        if (mLayoutSize == LAYOUT_MEDIUM) {
+            if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
+            int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
+            int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
+            views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+                    verticalPadding);
+        }
         return views;
     }
 
@@ -551,9 +571,16 @@
             views.setViewVisibility(R.id.predefined_icon, View.GONE);
             views.setViewVisibility(R.id.messages_count, View.GONE);
         }
-        String status = PeopleSpaceUtils.getLastInteractionString(mContext,
+        String status = getLastInteractionString(mContext,
                 mTile.getLastInteractionTimestamp());
-        views.setTextViewText(R.id.last_interaction, status);
+        if (status != null) {
+            if (DEBUG) Log.d(TAG, "Show last interaction");
+            views.setViewVisibility(R.id.last_interaction, View.VISIBLE);
+            views.setTextViewText(R.id.last_interaction, status);
+        } else {
+            if (DEBUG) Log.d(TAG, "Hide last interaction");
+            views.setViewVisibility(R.id.last_interaction, View.GONE);
+        }
         return views;
     }
 
@@ -599,4 +626,34 @@
                 hasNewStory);
         return convertDrawableToBitmap(personDrawable);
     }
+
+    /** Returns a readable status describing the {@code lastInteraction}. */
+    @Nullable
+    public static String getLastInteractionString(Context context, long lastInteraction) {
+        if (lastInteraction == 0L) {
+            Log.e(TAG, "Could not get valid last interaction");
+            return null;
+        }
+        long now = System.currentTimeMillis();
+        Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
+        MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
+                MeasureFormat.FormatWidth.WIDE);
+        if (durationSinceLastInteraction.toDays() <= ONE_DAY) {
+            return null;
+        } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
+            return context.getString(R.string.timestamp, formatter.formatMeasures(
+                    new Measure(durationSinceLastInteraction.toHours(),
+                            MeasureUnit.DAY)));
+        } else if (durationSinceLastInteraction.toDays() <= DAYS_IN_A_WEEK * 2) {
+            return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
+                            ? R.string.timestamp : R.string.over_timestamp,
+                    formatter.formatMeasures(
+                            new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
+                                    MeasureUnit.WEEK)));
+        } else {
+            // Over 2 weeks ago
+            return context.getString(R.string.over_timestamp,
+                    formatter.formatMeasures(new Measure(MAX_WEEKS, MeasureUnit.WEEK)));
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index c01a52d..c416b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -23,11 +23,13 @@
 import android.os.Bundle;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.notification.NotificationStats;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.statusbar.IStatusBarService;
@@ -48,15 +50,17 @@
     private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
     private NotificationEntryManager mNotificationEntryManager;
     private final Optional<BubblesManager> mBubblesManagerOptional;
+    private final UserManager mUserManager;
     private boolean mIsForTesting;
     private IStatusBarService mIStatusBarService;
 
     @Inject
     public LaunchConversationActivity(NotificationEntryManager notificationEntryManager,
-            Optional<BubblesManager> bubblesManagerOptional) {
+            Optional<BubblesManager> bubblesManagerOptional, UserManager userManager) {
         super();
         mNotificationEntryManager = notificationEntryManager;
         mBubblesManagerOptional = bubblesManagerOptional;
+        mUserManager = userManager;
     }
 
     @Override
@@ -80,12 +84,24 @@
             }
             mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_CLICKED);
             try {
+
+                if (mUserManager.isQuietModeEnabled(userHandle)) {
+                    if (DEBUG) Log.d(TAG, "Cannot launch app when quieted");
+                    final Intent dialogIntent =
+                            UnlaunchableAppActivity.createInQuietModeDialogIntent(
+                                    userHandle.getIdentifier());
+                    this.getApplicationContext().startActivity(dialogIntent);
+                    finish();
+                    return;
+                }
+
                 NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
                         notificationKey);
                 if (entry != null && entry.canBubble() && mBubblesManagerOptional.isPresent()) {
                     if (DEBUG) Log.d(TAG, "Open bubble for conversation");
                     mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
                     // Just opt-out and don't cancel the notification for bubbles.
+                    finish();
                     return;
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index d63dc4a..6cb7c31 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -31,7 +31,9 @@
 import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
 import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
@@ -51,7 +53,9 @@
 import android.os.Bundle;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.preference.PreferenceManager;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
@@ -76,6 +80,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -96,6 +101,8 @@
     private NotificationEntryManager mNotificationEntryManager;
     private PackageManager mPackageManager;
     private PeopleSpaceWidgetProvider mPeopleSpaceWidgetProvider;
+    private INotificationManager mINotificationManager;
+    private UserManager mUserManager;
     public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
     @GuardedBy("mLock")
     public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener>
@@ -121,17 +128,21 @@
         mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mPackageManager = mContext.getPackageManager();
         mPeopleSpaceWidgetProvider = new PeopleSpaceWidgetProvider();
+        mINotificationManager = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mUserManager = context.getSystemService(UserManager.class);
     }
 
     /**
      * AppWidgetManager setter used for testing.
      */
     @VisibleForTesting
-    protected void setAppWidgetManager(
+    public void setAppWidgetManager(
             AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
             PeopleManager peopleManager, LauncherApps launcherApps,
             NotificationEntryManager notificationEntryManager, PackageManager packageManager,
-            boolean isForTesting, PeopleSpaceWidgetProvider peopleSpaceWidgetProvider) {
+            boolean isForTesting, PeopleSpaceWidgetProvider peopleSpaceWidgetProvider,
+            UserManager userManager, INotificationManager notificationManager) {
         mAppWidgetManager = appWidgetManager;
         mIPeopleManager = iPeopleManager;
         mPeopleManager = peopleManager;
@@ -140,6 +151,8 @@
         mPackageManager = packageManager;
         mIsForTesting = isForTesting;
         mPeopleSpaceWidgetProvider = peopleSpaceWidgetProvider;
+        mUserManager = userManager;
+        mINotificationManager = notificationManager;
     }
 
     /**
@@ -783,4 +796,52 @@
         ComponentName componentName = new ComponentName(mContext, PeopleSpaceWidgetProvider.class);
         return mAppWidgetManager.requestPinAppWidget(componentName, extras, successCallback);
     }
+
+    /** Returns a list of map entries corresponding to user's priority conversations. */
+    @NonNull
+    public List<PeopleSpaceTile> getPriorityTiles()
+            throws Exception {
+        List<ConversationChannelWrapper> conversations =
+                mINotificationManager.getConversations(true).getList();
+        // Add priority conversations to tiles list.
+        Stream<ShortcutInfo> priorityConversations = conversations.stream()
+                .filter(c -> c.getNotificationChannel() != null
+                        && c.getNotificationChannel().isImportantConversation())
+                .map(c -> c.getShortcutInfo());
+        List<PeopleSpaceTile> priorityTiles = PeopleSpaceUtils.getSortedTiles(mIPeopleManager,
+                mLauncherApps, mUserManager,
+                priorityConversations);
+        priorityTiles = PeopleSpaceUtils.augmentTilesFromVisibleNotifications(
+                mContext, priorityTiles, mNotificationEntryManager);
+        return priorityTiles;
+    }
+
+    /** Returns a list of map entries corresponding to user's recent conversations. */
+    @NonNull
+    public List<PeopleSpaceTile> getRecentTiles()
+            throws Exception {
+        if (DEBUG) Log.d(TAG, "Add recent conversations");
+        List<ConversationChannelWrapper> conversations =
+                mINotificationManager.getConversations(false).getList();
+        Stream<ShortcutInfo> nonPriorityConversations = conversations.stream()
+                .filter(c -> c.getNotificationChannel() == null
+                        || !c.getNotificationChannel().isImportantConversation())
+                .map(c -> c.getShortcutInfo());
+
+        List<ConversationChannel> recentConversationsList =
+                mIPeopleManager.getRecentConversations().getList();
+        Stream<ShortcutInfo> recentConversations = recentConversationsList
+                .stream()
+                .map(c -> c.getShortcutInfo());
+
+        Stream<ShortcutInfo> mergedStream = Stream.concat(nonPriorityConversations,
+                recentConversations);
+        List<PeopleSpaceTile> recentTiles =
+                PeopleSpaceUtils.getSortedTiles(mIPeopleManager, mLauncherApps, mUserManager,
+                        mergedStream);
+
+        recentTiles = PeopleSpaceUtils.augmentTilesFromVisibleNotifications(
+                mContext, recentTiles, mNotificationEntryManager);
+        return recentTiles;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index f87ea7c..feb27d80 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -29,6 +29,7 @@
 import androidx.annotation.MainThread
 import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
+import com.android.systemui.appops.AppOpsController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -48,7 +49,6 @@
         return PrivacyDialog(context, list, starter)
     }
 }
-
 /**
  * Controller for [PrivacyDialog].
  *
@@ -66,6 +66,7 @@
     private val uiExecutor: Executor,
     private val privacyLogger: PrivacyLogger,
     private val keyguardStateController: KeyguardStateController,
+    private val appOpsController: AppOpsController,
     @VisibleForTesting private val dialogProvider: DialogProvider
 ) {
 
@@ -79,7 +80,8 @@
         @Background backgroundExecutor: Executor,
         @Main uiExecutor: Executor,
         privacyLogger: PrivacyLogger,
-        keyguardStateController: KeyguardStateController
+        keyguardStateController: KeyguardStateController,
+        appOpsController: AppOpsController
     ) : this(
             permissionManager,
             packageManager,
@@ -90,6 +92,7 @@
             uiExecutor,
             privacyLogger,
             keyguardStateController,
+            appOpsController,
             defaultDialogProvider
     )
 
@@ -127,7 +130,9 @@
     }
 
     @WorkerThread
-    private fun permGroupUsage(): List<PermGroupUsage> = permissionManager.indicatorAppOpUsageData
+    private fun permGroupUsage(): List<PermGroupUsage> {
+        return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
+    }
 
     /**
      * Show the [PrivacyDialog]
@@ -261,4 +266,4 @@
             starter: (String, Int) -> Unit
         ): PrivacyDialog
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 4c617ed..63ec6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -54,7 +54,8 @@
 data class PrivacyItem(
     val privacyType: PrivacyType,
     val application: PrivacyApplication,
-    val timeStampElapsed: Long = UNKNOWN_TIMESTAMP
+    val timeStampElapsed: Long = UNKNOWN_TIMESTAMP,
+    val paused: Boolean = false
 ) {
     val log = "(${privacyType.logName}, ${application.packageName}(${application.uid}), " +
             "$timeStampElapsed)"
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index f7e2a31..8b27b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -257,7 +257,7 @@
             privacyList = emptyList()
             return
         }
-        val list = appOpsController.getActiveAppOpsForUser(UserHandle.USER_ALL).filter {
+        val list = appOpsController.getActiveAppOps(true).filter {
             UserHandle.getUserId(it.uid) in currentUserIds ||
                     it.code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
                     it.code == AppOpsManager.OP_PHONE_CALL_CAMERA
@@ -279,7 +279,9 @@
 
         // Anything earlier than this timestamp can be removed
         val removeBeforeTime = systemClock.elapsedRealtime() - TIME_TO_HOLD_INDICATORS
-        val mustKeep = privacyList.filter { it.timeStampElapsed > removeBeforeTime && it !in list }
+        val mustKeep = privacyList.filter {
+            it.timeStampElapsed > removeBeforeTime && !(it isIn list)
+        }
 
         // There are items we must keep because they haven't been around for enough time.
         if (mustKeep.isNotEmpty()) {
@@ -291,7 +293,18 @@
             logger.logPrivacyItemsUpdateScheduled(delay)
             holdingRunnableCanceler = bgExecutor.executeDelayed(updateListAndNotifyChanges, delay)
         }
-        return list + mustKeep
+        return list.filter { !it.paused } + mustKeep
+    }
+
+    /**
+     * Ignores the paused status to determine if the element is in the list
+     */
+    private infix fun PrivacyItem.isIn(list: List<PrivacyItem>): Boolean {
+        return list.any {
+            it.privacyType == privacyType &&
+                    it.application == application &&
+                    it.timeStampElapsed == timeStampElapsed
+        }
     }
 
     private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
@@ -308,7 +321,7 @@
             return null
         }
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
-        return PrivacyItem(type, app, appOpItem.timeStartedElapsed)
+        return PrivacyItem(type, app, appOpItem.timeStartedElapsed, appOpItem.isDisabled)
     }
 
     interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 3467838..74ae3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -32,6 +32,7 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.globalactions.GlobalActionsDialogLite;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
@@ -65,6 +66,7 @@
     private final MetricsLogger mMetricsLogger;
     private final FalsingManager mFalsingManager;
     private final SettingsButton mSettingsButton;
+    private final View mSettingsButtonContainer;
     private final TextView mBuildText;
     private final View mEdit;
     private final MultiUserSwitch mMultiUserSwitch;
@@ -152,6 +154,7 @@
         mFalsingManager = falsingManager;
 
         mSettingsButton = mView.findViewById(R.id.settings_button);
+        mSettingsButtonContainer = mView.findViewById(R.id.settings_button_container);
         mBuildText = mView.findViewById(R.id.build);
         mEdit = mView.findViewById(android.R.id.edit);
         mMultiUserSwitch = mView.findViewById(R.id.multi_user_switch);
@@ -258,10 +261,12 @@
         mView.disable(state2, isTunerEnabled());
     }
 
-
     private void startSettingsActivity() {
+        ActivityLaunchAnimator.Controller animationController =
+                mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
+                        mSettingsButtonContainer) : null;
         mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
-                true /* dismissShade */);
+                true /* dismissShade */, animationController);
     }
 
     private boolean isTunerEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index b728b43..6f19276 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -184,8 +184,7 @@
 
     @Override
     public CharSequence getTileLabel() {
-        CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
-        return qawLabel == null ? mLabel : qawLabel;
+        return mLabel;
     }
 
     private void queryWalletCards() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index dbd6758..b60fd13 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -48,7 +48,7 @@
         super(context, attrs);
     }
 
-    // Inflated from quick_settings_brightness_dialog or quick_settings_brightness_dialog_thick
+    // Inflated from quick_settings_brightness_dialog
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
new file mode 100644
index 0000000..5ab71bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -0,0 +1,352 @@
+/*
+ * 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.statusbar.events
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.annotation.UiThread
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.widget.FrameLayout
+
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+
+import java.lang.IllegalStateException
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Understands how to keep the persistent privacy dot in the corner of the screen in
+ * ScreenDecorations, which does not rotate with the device.
+ *
+ * The basic principle here is that each dot will sit in a box that is equal to the margins of the
+ * status bar (specifically the status_bar_contents view in PhoneStatusBarView). Each dot container
+ * will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
+ * the contained ImageView will be set to center_vertical and away from the corner horizontally. The
+ * Views will match the status bar top padding and status bar height so that the dot can appear to
+ * reside directly after the status bar system contents (basically to the right of the battery).
+ *
+ * NOTE: any operation that modifies views directly must run on the provided executor, because
+ * these views are owned by ScreenDecorations and it runs in its own thread
+ */
+
+@SysUISingleton
+class PrivacyDotViewController @Inject constructor(
+    @Main val mainExecutor: Executor,
+    val animationScheduler: SystemStatusAnimationScheduler
+) {
+    private var rotation = 0
+    private var leftSize = 0
+    private var rightSize = 0
+
+    private var sbHeightPortrait = 0
+    private var sbHeightLandscape = 0
+
+    private var hasMultipleHeights = false
+    private var needsHeightUpdate = false
+    private var needsRotationUpdate = false
+    private var needsMarginUpdate = false
+
+    private lateinit var tl: View
+    private lateinit var tr: View
+    private lateinit var bl: View
+    private lateinit var br: View
+
+    // Track which corner is active (based on orientation + RTL)
+    private var designatedCorner: View? = null
+
+    // Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
+    private var uiExecutor: Executor? = null
+
+    private val views: Sequence<View>
+        get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
+
+    fun setUiExecutor(e: Executor) {
+        uiExecutor = e
+    }
+
+    @UiThread
+    fun updateRotation(rot: Int) {
+        if (rot == rotation) {
+            return
+        }
+
+        // A rotation has started, hide the views to avoid flicker
+        setCornerVisibilities(View.INVISIBLE)
+
+        if (hasMultipleHeights && (rotation % 2) != (rot % 2)) {
+            // we've changed from vertical to horizontal; update status bar height
+            needsHeightUpdate = true
+        }
+
+        rotation = rot
+        needsRotationUpdate = true
+    }
+
+    @UiThread
+    private fun updateHeights(rot: Int) {
+        val height = when (rot) {
+            0, 2 -> sbHeightPortrait
+            1, 3 -> sbHeightLandscape
+            else -> 0
+        }
+
+        views.forEach { it.layoutParams.height = height }
+    }
+
+    // Update the gravity and margins of the privacy views
+    @UiThread
+    private fun updateRotations() {
+        // To keep a view in the corner, its gravity is always the description of its current corner
+        // Therefore, just figure out which view is in which corner. This turns out to be something
+        // like (myCorner - rot) mod 4, where topLeft = 0, topRight = 1, etc. and portrait = 0, and
+        // rotating the device counter-clockwise increments rotation by 1
+
+        views.forEach { corner ->
+            val rotatedCorner = rotatedCorner(cornerForView(corner))
+            (corner.layoutParams as FrameLayout.LayoutParams).apply {
+                gravity = rotatedCorner.toGravity()
+            }
+
+            // Set the dot's view gravity to hug the status bar
+            (corner.findViewById<View>(R.id.privacy_dot)
+                    .layoutParams as FrameLayout.LayoutParams)
+                        .gravity = rotatedCorner.innerGravity()
+        }
+    }
+
+    @UiThread
+    private fun updateCornerSizes() {
+        views.forEach { corner ->
+            val rotatedCorner = rotatedCorner(cornerForView(corner))
+            val w = widthForCorner(rotatedCorner)
+            Log.d(TAG, "updateCornerSizes: setting (${cornerForView(corner)}) to $w")
+            (corner.layoutParams as FrameLayout.LayoutParams).width = w
+            corner.requestLayout()
+        }
+    }
+
+    // Designated view will be the one at statusbar's view.END
+    @UiThread
+    private fun selectDesignatedCorner(): View? {
+        if (!this::tl.isInitialized) {
+            return null
+        }
+
+        val isRtl = tl.isLayoutRtl
+
+        return when (rotation) {
+            0 -> if (isRtl) tl else tr
+            1 -> if (isRtl) tr else br
+            2 -> if (isRtl) br else bl
+            3 -> if (isRtl) bl else tl
+            else -> throw IllegalStateException("unknown rotation")
+        }
+    }
+
+    // Track the current designated corner and maybe animate to a new rotation
+    @UiThread
+    private fun updateDesignatedCorner(newCorner: View) {
+        designatedCorner = newCorner
+
+        if (animationScheduler.hasPersistentDot) {
+            designatedCorner!!.visibility = View.VISIBLE
+            designatedCorner!!.alpha = 0f
+            designatedCorner!!.animate()
+                    .alpha(1.0f)
+                    .setDuration(300)
+                    .start()
+        }
+    }
+
+    @UiThread
+    private fun setCornerVisibilities(vis: Int) {
+        views.forEach { corner ->
+            corner.visibility = vis
+        }
+    }
+
+    private fun cornerForView(v: View): Int {
+        return when (v) {
+            tl -> TOP_LEFT
+            tr -> TOP_RIGHT
+            bl -> BOTTOM_LEFT
+            br -> BOTTOM_RIGHT
+            else -> throw IllegalArgumentException("not a corner view")
+        }
+    }
+
+    private fun rotatedCorner(corner: Int): Int {
+        var modded = corner - rotation
+        if (modded < 0) {
+            modded += 4
+        }
+
+        return modded
+    }
+
+    private fun widthForCorner(corner: Int): Int {
+        return when (corner) {
+            TOP_LEFT, BOTTOM_LEFT -> leftSize
+            TOP_RIGHT, BOTTOM_RIGHT -> rightSize
+            else -> throw IllegalArgumentException("Unknown corner")
+        }
+    }
+
+    fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
+        if (this::tl.isInitialized && this::tr.isInitialized &&
+                this::bl.isInitialized && this::br.isInitialized) {
+            if (tl == topLeft && tr == topRight && bl == bottomLeft && br == bottomRight) {
+                return
+            }
+        }
+
+        tl = topLeft
+        tr = topRight
+        bl = bottomLeft
+        br = bottomRight
+
+        designatedCorner = selectDesignatedCorner()
+        mainExecutor.execute {
+            animationScheduler.addCallback(systemStatusAnimationCallback)
+        }
+    }
+
+    /**
+     * Set the status bar height in portrait and landscape, in pixels. If they are the same you can
+     * pass the same value twice
+     */
+    fun setStatusBarHeights(portrait: Int, landscape: Int) {
+        sbHeightPortrait = portrait
+        sbHeightLandscape = landscape
+
+        hasMultipleHeights = portrait != landscape
+    }
+
+    /**
+     * The dot view containers will fill the margin in order to position the dots correctly
+     *
+     * @param left the space between the status bar contents and the left side of the screen
+     * @param right space between the status bar contents and the right side of the screen
+     */
+    fun setStatusBarMargins(left: Int, right: Int) {
+        leftSize = left
+        rightSize = right
+
+        needsMarginUpdate = true
+
+        // Margins come after PhoneStatusBarView does a layout pass, and so will always happen
+        // after rotation changes. It is safe to execute the updates from here
+        uiExecutor?.execute {
+            doUpdates(needsRotationUpdate, needsHeightUpdate, needsMarginUpdate)
+        }
+    }
+
+    private fun doUpdates(rot: Boolean, height: Boolean, width: Boolean) {
+        var newDesignatedCorner: View? = null
+
+        if (rot) {
+            needsRotationUpdate = false
+            updateRotations()
+            newDesignatedCorner = selectDesignatedCorner()
+        }
+
+        if (height) {
+            needsHeightUpdate = false
+            updateHeights(rotation)
+        }
+
+        if (width) {
+            needsMarginUpdate = false
+            updateCornerSizes()
+        }
+
+        if (newDesignatedCorner != null && newDesignatedCorner != designatedCorner) {
+            updateDesignatedCorner(newDesignatedCorner)
+        }
+    }
+
+    private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
+            object : SystemStatusAnimationCallback {
+        override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
+            if (designatedCorner == null) {
+                return null
+            }
+
+            val alpha = ObjectAnimator.ofFloat(
+                    designatedCorner, "alpha", 0f, 1f)
+            alpha.duration = DURATION
+            alpha.interpolator = Interpolators.ALPHA_OUT
+            alpha.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationStart(animator: Animator) {
+                    uiExecutor?.execute { designatedCorner?.visibility = View.VISIBLE }
+                }
+            })
+            return alpha
+        }
+
+        override fun onHidePersistentDot(): Animator? {
+            if (designatedCorner == null) {
+                return null
+            }
+
+            val alpha = ObjectAnimator.ofFloat(
+                    designatedCorner, "alpha", 1f, 0f)
+            alpha.duration = DURATION
+            alpha.interpolator = Interpolators.ALPHA_OUT
+            alpha.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animator: Animator) {
+                    uiExecutor?.execute { designatedCorner?.visibility = View.INVISIBLE }
+                }
+            })
+            alpha.start()
+            return null
+        }
+    }
+}
+
+const val TOP_LEFT = 0
+const val TOP_RIGHT = 1
+const val BOTTOM_RIGHT = 2
+const val BOTTOM_LEFT = 3
+private const val DURATION = 160L
+private const val TAG = "PrivacyDotViewController"
+
+private fun Int.toGravity(): Int {
+    return when (this) {
+        TOP_LEFT -> Gravity.TOP or Gravity.LEFT
+        TOP_RIGHT -> Gravity.TOP or Gravity.RIGHT
+        BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
+        BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
+        else -> throw IllegalArgumentException("Not a corner")
+    }
+}
+
+private fun Int.innerGravity(): Int {
+    return when (this) {
+        TOP_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
+        TOP_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
+        BOTTOM_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
+        BOTTOM_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
+        else -> throw IllegalArgumentException("Not a corner")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
new file mode 100644
index 0000000..6380dc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.statusbar.events
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.ImageView
+import com.android.settingslib.graph.ThemedBatteryDrawable
+import com.android.systemui.R
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyItem
+
+interface StatusEvent {
+    val priority: Int
+    // Whether or not to force the status bar open and show a dot
+    val forceVisible: Boolean
+    val viewCreator: (context: Context) -> View
+}
+
+class BatteryEvent : StatusEvent {
+    override val priority = 50
+    override val forceVisible = false
+
+    override val viewCreator: (context: Context) -> View = { context ->
+        val iv = ImageView(context)
+        iv.setImageDrawable(ThemedBatteryDrawable(context, Color.WHITE))
+        iv.setBackgroundDrawable(ColorDrawable(Color.GREEN))
+        iv
+    }
+
+    override fun toString(): String {
+        return javaClass.simpleName
+    }
+}
+class PrivacyEvent : StatusEvent {
+    override val priority = 100
+    override val forceVisible = true
+    var privacyItems: List<PrivacyItem> = listOf()
+
+    override val viewCreator: (context: Context) -> View = { context ->
+        val v = LayoutInflater.from(context)
+                .inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip
+        v.privacyList = privacyItems
+        v
+    }
+
+    override fun toString(): String {
+        return javaClass.simpleName
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
new file mode 100644
index 0000000..6209630
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.statusbar.events
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+
+import com.android.systemui.R
+import com.android.systemui.statusbar.SuperStatusBarViewFactory
+import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.phone.StatusBarWindowView
+
+import javax.inject.Inject
+
+/**
+ * //TODO: this _probably_ doesn't control a window anymore
+ * Controls the window for system event animations.
+ */
+class SystemEventChipAnimationController @Inject constructor(
+    private val context: Context,
+    private val statusBarViewFactory: SuperStatusBarViewFactory,
+    private val statusBarWindowController: StatusBarWindowController
+) : SystemStatusChipAnimationCallback {
+    var showPersistentDot = false
+        set(value) {
+            field = value
+            statusBarWindowController.setForceStatusBarVisible(value)
+            maybeUpdateShowDot()
+        }
+
+    private lateinit var animationWindowView: FrameLayout
+    private lateinit var animationDotView: View
+    private lateinit var statusBarWindowView: StatusBarWindowView
+    private var currentAnimatedView: View? = null
+
+    // TODO: move to dagger
+    private var initialized = false
+
+    override fun onChipAnimationStart(
+        viewCreator: (context: Context) -> View,
+        @SystemAnimationState state: Int
+    ) {
+        if (!initialized) init()
+
+        if (state == ANIMATING_IN) {
+            currentAnimatedView = viewCreator(context)
+            animationWindowView.addView(currentAnimatedView, layoutParamsDefault)
+
+            // We are animating IN; chip comes in from View.END
+            currentAnimatedView?.apply {
+                translationX = width.toFloat()
+                alpha = 0f
+                visibility = View.VISIBLE
+            }
+        } else {
+            // We are animating away
+            currentAnimatedView?.apply {
+                translationX = 0f
+                alpha = 1f
+            }
+        }
+    }
+
+    override fun onChipAnimationEnd(@SystemAnimationState state: Int) {
+        if (state == ANIMATING_IN) {
+            // Finished animating in
+            currentAnimatedView?.apply {
+                translationX = 0f
+                alpha = 1f
+            }
+        } else {
+            // Finished animating away
+            currentAnimatedView?.apply {
+                visibility = View.INVISIBLE
+            }
+            animationWindowView.removeView(currentAnimatedView)
+        }
+    }
+
+    override fun onChipAnimationUpdate(
+        animator: ValueAnimator,
+        @SystemAnimationState state: Int
+    ) {
+        // Alpha is parameterized 0,1, and translation from (width, 0)
+        currentAnimatedView?.apply {
+            val amt = animator.animatedValue as Float
+
+            alpha = amt
+
+            val w = width
+            val translation = (1 - amt) * w
+            translationX = translation
+        }
+    }
+
+    private fun maybeUpdateShowDot() {
+        if (!initialized) return
+        if (!showPersistentDot && currentAnimatedView == null) {
+            animationDotView.visibility = View.INVISIBLE
+        }
+    }
+
+    private fun init() {
+        initialized = true
+        statusBarWindowView = statusBarViewFactory.statusBarWindowView
+        animationWindowView = LayoutInflater.from(context)
+                .inflate(R.layout.system_event_animation_window, null) as FrameLayout
+        animationDotView = animationWindowView.findViewById(R.id.dot_view)
+        val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+        lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
+        statusBarWindowView.addView(animationWindowView, lp)
+    }
+
+    private val layoutParamsDefault = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
+        it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
new file mode 100644
index 0000000..b481823
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.statusbar.events
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.statusbar.policy.BatteryController
+import javax.inject.Inject
+
+/**
+ * Listens for system events (battery, privacy, connectivity) and allows listeners
+ * to show status bar animations when they happen
+ */
+@SysUISingleton
+class SystemEventCoordinator @Inject constructor(
+    private val batteryController: BatteryController,
+    private val privacyController: PrivacyItemController
+) {
+    private lateinit var scheduler: SystemStatusAnimationScheduler
+
+    fun startObserving() {
+        /* currently unused
+        batteryController.addCallback(batteryStateListener)
+        */
+        privacyController.addCallback(privacyStateListener)
+    }
+
+    fun stopObserving() {
+        /* currently unused
+        batteryController.removeCallback(batteryStateListener)
+        */
+        privacyController.removeCallback(privacyStateListener)
+    }
+
+    fun attachScheduler(s: SystemStatusAnimationScheduler) {
+        this.scheduler = s
+    }
+
+    fun notifyPluggedIn() {
+        scheduler.onStatusEvent(BatteryEvent())
+    }
+
+    fun notifyPrivacyItemsEmpty() {
+        scheduler.setShouldShowPersistentPrivacyIndicator(false)
+    }
+
+    fun notifyPrivacyItemsChanged() {
+        val event = PrivacyEvent()
+        event.privacyItems = privacyStateListener.currentPrivacyItems
+        scheduler.onStatusEvent(event)
+    }
+
+    private val batteryStateListener = object : BatteryController.BatteryStateChangeCallback {
+        var plugged = false
+        var stateKnown = false
+        override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+            if (!stateKnown) {
+                stateKnown = true
+                plugged = pluggedIn
+                notifyListeners()
+                return
+            }
+
+            if (plugged != pluggedIn) {
+                plugged = pluggedIn
+                notifyListeners()
+            }
+        }
+
+        private fun notifyListeners() {
+            // We only care about the plugged in status
+            if (plugged) notifyPluggedIn()
+        }
+    }
+
+    private val privacyStateListener = object : PrivacyItemController.Callback {
+        var currentPrivacyItems = listOf<PrivacyItem>()
+
+        override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+            if (privacyItems.isNotEmpty() && currentPrivacyItems.containsAll(privacyItems)) {
+                return
+            }
+            currentPrivacyItems = privacyItems
+            notifyListeners()
+        }
+
+        private fun notifyListeners() {
+            if (currentPrivacyItems.isEmpty()) {
+                notifyPrivacyItemsEmpty()
+            } else {
+                notifyPrivacyItemsChanged()
+            }
+        }
+    }
+}
+
+private const val TAG = "SystemEventCoordinator"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
new file mode 100644
index 0000000..b85d031
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -0,0 +1,333 @@
+/*
+ * 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.statusbar.events
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.annotation.IntDef
+import android.content.Context
+import android.os.Process
+import android.util.Log
+import android.view.View
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.util.Assert
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
+
+import javax.inject.Inject
+
+/**
+ * Dead-simple scheduler for system status events. Obeys the following principles (all values TBD):
+ *      - Avoiding log spam by only allowing 12 events per minute (1event/5s)
+ *      - Waits 100ms to schedule any event for debouncing/prioritization
+ *      - Simple prioritization: Privacy > Battery > connectivity (encoded in StatusEvent)
+ *      - Only schedules a single event, and throws away lowest priority events
+ *
+ * There are 4 basic stages of animation at play here:
+ *      1. System chrome animation OUT
+ *      2. Chip animation IN
+ *      3. Chip animation OUT; potentially into a dot
+ *      4. System chrome animation IN
+ *
+ * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system
+ * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize
+ * their respective views based on the progress of the animator. Interpolation differences TBD
+ */
+@SysUISingleton
+class SystemStatusAnimationScheduler @Inject constructor(
+    private val coordinator: SystemEventCoordinator,
+    private val chipAnimationController: SystemEventChipAnimationController,
+    private val statusBarWindowController: StatusBarWindowController,
+    private val systemClock: SystemClock,
+    @Main private val executor: DelayableExecutor
+) : CallbackController<SystemStatusAnimationCallback> {
+
+    /** True from the time a scheduled event starts until it's animation finishes */
+    var isActive = false
+        private set
+
+    @SystemAnimationState var animationState: Int = IDLE
+        private set
+
+    /** True if the persistent privacy dot should be active */
+    var hasPersistentDot = false
+        private set
+
+    private var scheduledEvent: StatusEvent? = null
+    private var cancelExecutionRunnable: Runnable? = null
+    private val listeners = mutableSetOf<SystemStatusAnimationCallback>()
+
+    init {
+        coordinator.attachScheduler(this)
+    }
+
+    fun onStatusEvent(event: StatusEvent) {
+        // Ignore any updates until the system is up and running
+        if (isTooEarly()) {
+            return
+        }
+
+        // Don't deal with threading for now (no need let's be honest)
+        Assert.isMainThread()
+        if (event.priority > scheduledEvent?.priority ?: -1) {
+            if (DEBUG) {
+                Log.d(TAG, "scheduling event $event")
+            }
+            scheduleEvent(event)
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "ignoring event $event")
+            }
+        }
+    }
+
+    private fun clearDotIfVisible() {
+        notifyHidePersistentDot()
+    }
+
+    fun setShouldShowPersistentPrivacyIndicator(should: Boolean) {
+        if (hasPersistentDot == should) {
+            return
+        }
+
+        hasPersistentDot = should
+
+        if (!hasPersistentDot) {
+            clearDotIfVisible()
+        }
+    }
+
+    private fun isTooEarly(): Boolean {
+        Log.d(TAG, "time=> ${systemClock.uptimeMillis() - Process.getStartUptimeMillis()}")
+        return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME
+    }
+
+    /**
+     * Clear the scheduled event (if any) and schedule a new one
+     */
+    private fun scheduleEvent(event: StatusEvent) {
+        scheduledEvent = event
+        if (scheduledEvent!!.forceVisible) {
+            hasPersistentDot = true
+        }
+
+        // Schedule the animation to start after a debounce period
+        cancelExecutionRunnable = executor.executeDelayed({
+            cancelExecutionRunnable = null
+            animationState = ANIMATING_IN
+            statusBarWindowController.setForceStatusBarVisible(true)
+
+            val entranceAnimator = ValueAnimator.ofFloat(1f, 0f)
+            entranceAnimator.duration = ENTRANCE_ANIM_LENGTH
+            entranceAnimator.addListener(systemAnimatorAdapter)
+            entranceAnimator.addUpdateListener(systemUpdateListener)
+
+            val chipAnimator = ValueAnimator.ofFloat(0f, 1f)
+            chipAnimator.duration = CHIP_ANIM_LENGTH
+            chipAnimator.addListener(
+                    ChipAnimatorAdapter(RUNNING_CHIP_ANIM, scheduledEvent!!.viewCreator))
+            chipAnimator.addUpdateListener(chipUpdateListener)
+
+            val aSet2 = AnimatorSet()
+            aSet2.playSequentially(entranceAnimator, chipAnimator)
+            aSet2.start()
+
+            executor.executeDelayed({
+                animationState = ANIMATING_OUT
+
+                val systemAnimator = ValueAnimator.ofFloat(0f, 1f)
+                systemAnimator.duration = ENTRANCE_ANIM_LENGTH
+                systemAnimator.addListener(systemAnimatorAdapter)
+                systemAnimator.addUpdateListener(systemUpdateListener)
+
+                val chipAnimator = ValueAnimator.ofFloat(1f, 0f)
+                chipAnimator.duration = CHIP_ANIM_LENGTH
+                chipAnimator.addListener(ChipAnimatorAdapter(IDLE, scheduledEvent!!.viewCreator))
+                chipAnimator.addUpdateListener(chipUpdateListener)
+
+                val aSet2 = AnimatorSet()
+
+                aSet2.play(chipAnimator).before(systemAnimator)
+                if (hasPersistentDot) {
+                    val dotAnim = notifyTransitionToPersistentDot()
+                    if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
+                }
+
+                aSet2.start()
+
+                statusBarWindowController.setForceStatusBarVisible(false)
+                scheduledEvent = null
+            }, DISPLAY_LENGTH)
+        }, DELAY)
+    }
+
+    private fun notifyTransitionToPersistentDot(): Animator? {
+        val anims: List<Animator> = listeners.mapNotNull {
+            it.onSystemStatusAnimationTransitionToPersistentDot()
+        }
+        if (anims.isNotEmpty()) {
+            val aSet = AnimatorSet()
+            aSet.playTogether(anims)
+            return aSet
+        }
+
+        return null
+    }
+
+    private fun notifyHidePersistentDot(): Animator? {
+        val anims: List<Animator> = listeners.mapNotNull {
+            it.onHidePersistentDot()
+        }
+
+        if (anims.isNotEmpty()) {
+            val aSet = AnimatorSet()
+            aSet.playTogether(anims)
+            return aSet
+        }
+
+        return null
+    }
+
+    private fun notifySystemStart() {
+        listeners.forEach { it.onSystemChromeAnimationStart() }
+    }
+
+    private fun notifySystemFinish() {
+        listeners.forEach { it.onSystemChromeAnimationEnd() }
+    }
+
+    private fun notifySystemAnimationUpdate(anim: ValueAnimator) {
+        listeners.forEach { it.onSystemChromeAnimationUpdate(anim) }
+    }
+
+    override fun addCallback(listener: SystemStatusAnimationCallback) {
+        Assert.isMainThread()
+
+        if (listeners.isEmpty()) {
+            coordinator.startObserving()
+        }
+        listeners.add(listener)
+    }
+
+    override fun removeCallback(listener: SystemStatusAnimationCallback) {
+        Assert.isMainThread()
+
+        listeners.remove(listener)
+        if (listeners.isEmpty()) {
+            coordinator.stopObserving()
+        }
+    }
+
+    private val systemUpdateListener = ValueAnimator.AnimatorUpdateListener {
+        anim -> notifySystemAnimationUpdate(anim)
+    }
+
+    private val systemAnimatorAdapter = object : AnimatorListenerAdapter() {
+        override fun onAnimationEnd(p0: Animator?) {
+            notifySystemFinish()
+        }
+
+        override fun onAnimationStart(p0: Animator?) {
+            notifySystemStart()
+        }
+    }
+
+    private val chipUpdateListener = ValueAnimator.AnimatorUpdateListener {
+        anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
+    }
+
+    inner class ChipAnimatorAdapter(
+        @SystemAnimationState val endState: Int,
+        val viewCreator: (context: Context) -> View
+    ) : AnimatorListenerAdapter() {
+        override fun onAnimationEnd(p0: Animator?) {
+            chipAnimationController.onChipAnimationEnd(animationState)
+            animationState = endState
+        }
+
+        override fun onAnimationStart(p0: Animator?) {
+            chipAnimationController.onChipAnimationStart(viewCreator, animationState)
+        }
+    }
+}
+
+/**
+ * The general idea here is that this scheduler will run two value animators, and provide
+ * animator-like callbacks for each kind of animation. The SystemChrome animation is expected to
+ * create space for the chip animation to display. This means hiding the system elements in the
+ * status bar and keyguard.
+ *
+ * TODO: the chip animation really only has one client, we can probably remove it from this
+ * interface
+ *
+ * The value animators themselves are simple animators from 0.0 to 1.0. Listeners can apply any
+ * interpolation they choose but realistically these are most likely to be simple alpha transitions
+ */
+interface SystemStatusAnimationCallback {
+    @JvmDefault fun onSystemChromeAnimationUpdate(animator: ValueAnimator) {}
+    @JvmDefault fun onSystemChromeAnimationStart() {}
+    @JvmDefault fun onSystemChromeAnimationEnd() {}
+
+    // Best method name, change my mind
+    @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
+    @JvmDefault fun onHidePersistentDot(): Animator? { return null }
+}
+
+interface SystemStatusChipAnimationCallback {
+    fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {}
+
+    fun onChipAnimationStart(
+        viewCreator: (context: Context) -> View,
+        @SystemAnimationState state: Int
+    ) {}
+
+    fun onChipAnimationEnd(@SystemAnimationState state: Int) {}
+}
+
+/**
+ */
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(
+        value = [
+            IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT
+        ]
+)
+annotation class SystemAnimationState
+
+/** No animation is in progress */
+const val IDLE = 0
+/** System is animating out, and chip is animating in */
+const val ANIMATING_IN = 1
+/** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */
+const val RUNNING_CHIP_ANIM = 2
+/** Chip is animating away and system is animating back */
+const val ANIMATING_OUT = 3
+
+private const val TAG = "SystemStatusAnimationScheduler"
+private const val DELAY: Long = 100
+private const val DISPLAY_LENGTH = 5000L
+private const val ENTRANCE_ANIM_LENGTH = 500L
+private const val CHIP_ANIM_LENGTH = 500L
+private const val MIN_UPTIME: Long = 5 * 1000
+
+private const val DEBUG = false
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
index 5748c4a..2537b19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.service.notification.StatusBarNotification;
+import android.view.View;
 
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -33,7 +34,8 @@
     void startNotificationGutsIntent(Intent intent, int appUid,
             ExpandableNotificationRow row);
 
-    void startHistoryIntent(boolean showHistory);
+    /** Called when the user clicks "Manage" or "History" in the Shade. */
+    void startHistoryIntent(View view, boolean showHistory);
 
     default boolean isCollapsingToShowActivityOverLockscreen() {
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 78fcf18c..f8543f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -170,8 +170,8 @@
     }
 
     private void updateColors() {
-        mNormalColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground)
-                .getDefaultColor();
+        mNormalColor = Utils.getColorAttrDefaultColor(mContext,
+                com.android.internal.R.attr.colorSurface);
         mTintedRippleColor = mContext.getColor(
                 R.color.notification_ripple_tinted_color);
         mNormalRippleColor = mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 4ed5056..3bf0ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -106,10 +106,6 @@
         showHistory(mShowHistory);
     }
 
-    public boolean isButtonVisible() {
-        return mManageButton.getAlpha() != 0.0f;
-    }
-
     @Override
     public ExpandableViewState createExpandableViewState() {
         return new FooterViewState();
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 733a9f6..751573a 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
@@ -4877,7 +4877,7 @@
             clearNotifications(ROWS_ALL, true /* closeShade */);
         });
         footerView.setManageButtonClickListener(v -> {
-            mNotificationActivityStarter.startHistoryIntent(mFooterView.isHistoryShown());
+            mNotificationActivityStarter.startHistoryIntent(v, mFooterView.isHistoryShown());
         });
         setFooterView(footerView);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 40e6b40..4e57e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -19,10 +19,16 @@
 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
 import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
 
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+
+import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.app.Fragment;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,6 +42,9 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
@@ -44,6 +53,8 @@
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -55,7 +66,8 @@
  * updated by the StatusBarIconController and DarkIconManager while it is attached.
  */
 public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
-        StatusBarStateController.StateListener {
+        StatusBarStateController.StateListener,
+        SystemStatusAnimationCallback {
 
     public static final String TAG = "CollapsedStatusBarFragment";
     private static final String EXTRA_PANEL_STATE = "panel_state";
@@ -78,6 +90,8 @@
     private View mOperatorNameFrame;
     private CommandQueue mCommandQueue;
     private OngoingCallController mOngoingCallController;
+    private final SystemStatusAnimationScheduler mAnimationScheduler;
+    private final PrivacyDotViewController mDotViewController;
 
     private List<String> mBlockedIcons = new ArrayList<>();
 
@@ -103,8 +117,14 @@
     };
 
     @Inject
-    public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) {
+    public CollapsedStatusBarFragment(
+            OngoingCallController ongoingCallController,
+            SystemStatusAnimationScheduler animationScheduler,
+            PrivacyDotViewController dotViewController
+    ) {
         mOngoingCallController = ongoingCallController;
+        mAnimationScheduler = animationScheduler;
+        mDotViewController = dotViewController;
     }
 
     @Override
@@ -127,6 +147,9 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mStatusBar = (PhoneStatusBarView) view;
+        View contents = mStatusBar.findViewById(R.id.status_bar_contents);
+        contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
+        updateStatusBarLocation(contents.getLeft(), contents.getRight());
         if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
             mStatusBar.restoreHierarchyState(
                     savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
@@ -143,6 +166,7 @@
         showClock(false);
         initEmergencyCryptkeeperText();
         initOperatorName();
+        mAnimationScheduler.addCallback(this);
     }
 
     @Override
@@ -208,6 +232,7 @@
         if (displayId != getContext().getDisplayId()) {
             return;
         }
+        Log.d(TAG, "disable: ");
         state1 = adjustDisableFlags(state1);
         final int old1 = mDisabled1;
         final int diff1 = state1 ^ old1;
@@ -292,19 +317,22 @@
         return false;
     }
 
-    public void hideSystemIconArea(boolean animate) {
+    private void hideSystemIconArea(boolean animate) {
         animateHide(mSystemIconArea, animate);
     }
 
-    public void showSystemIconArea(boolean animate) {
-        animateShow(mSystemIconArea, animate);
+    private void showSystemIconArea(boolean animate) {
+        // Only show the system icon area if we are not currently animating
+        if (mAnimationScheduler.getAnimationState() == IDLE) {
+            animateShow(mSystemIconArea, animate);
+        }
     }
 
-    public void hideClock(boolean animate) {
+    private void hideClock(boolean animate) {
         animateHiddenState(mClockView, clockHiddenMode(), animate);
     }
 
-    public void showClock(boolean animate) {
+    private void showClock(boolean animate) {
         animateShow(mClockView, animate);
     }
 
@@ -425,12 +453,60 @@
     }
 
     @Override
-    public void onStateChanged(int newState) {
-
-    }
+    public void onStateChanged(int newState) { }
 
     @Override
     public void onDozingChanged(boolean isDozing) {
         disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
     }
+
+    @Override
+    public void onSystemChromeAnimationStart() {
+        if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT
+                && !isSystemIconAreaDisabled()) {
+            mSystemIconArea.setVisibility(View.VISIBLE);
+            mSystemIconArea.setAlpha(0f);
+        }
+    }
+
+    @Override
+    public void onSystemChromeAnimationEnd() {
+        // Make sure the system icons are out of the way
+        if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
+            mSystemIconArea.setVisibility(View.INVISIBLE);
+            mSystemIconArea.setAlpha(0f);
+        } else {
+            if (isSystemIconAreaDisabled()) {
+                // don't unhide
+                return;
+            }
+
+            mSystemIconArea.setAlpha(1f);
+            mSystemIconArea.setVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    public void onSystemChromeAnimationUpdate(@NotNull ValueAnimator animator) {
+        mSystemIconArea.setAlpha((float) animator.getAnimatedValue());
+    }
+
+    private boolean isSystemIconAreaDisabled() {
+        return (mDisabled1 & DISABLE_SYSTEM_INFO) != 0 || (mDisabled2 & DISABLE2_SYSTEM_ICONS) != 0;
+    }
+
+    private void updateStatusBarLocation(int left, int right) {
+        int leftMargin = left - mStatusBar.getLeft();
+        int rightMargin = mStatusBar.getRight() - right;
+
+        mDotViewController.setStatusBarMargins(leftMargin, rightMargin);
+    }
+
+    // Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
+    private View.OnLayoutChangeListener mStatusBarLayoutListener =
+            (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                if (left != oldLeft || right != oldRight) {
+                    updateStatusBarLocation(left, right);
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index c3325b1..b83039b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -68,6 +68,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LockPatternUtils;
@@ -154,6 +155,7 @@
     private StatusBar mStatusBar;
     private KeyguardAffordanceHelper mAffordanceHelper;
     private FalsingManager mFalsingManager;
+    @Nullable private Executor mUiExecutor;
     private boolean mUserSetupComplete;
     private boolean mPrewarmBound;
     private Messenger mPrewarmMessenger;
@@ -429,7 +431,7 @@
     }
 
     private void updateWalletVisibility() {
-        if (mDozing || !mWalletEnabled) {
+        if (mDozing || !mWalletEnabled || !mHasCard) {
             mWalletButton.setVisibility(GONE);
         } else {
             mWalletButton.setVisibility(VISIBLE);
@@ -659,6 +661,13 @@
         updateCameraVisibility();
     }
 
+    @Override
+    public void onKeyguardShowingChanged() {
+        if (mKeyguardStateController.isShowing()) {
+            queryWalletCards();
+        }
+    }
+
     private void inflateCameraPreview() {
         View previewBefore = mCameraPreview;
         boolean visibleBefore = false;
@@ -897,18 +906,20 @@
     public void initWallet(QuickAccessWalletClient client, Executor uiExecutor, boolean enabled) {
         mQuickAccessWalletClient = client;
         mWalletEnabled = enabled && client.isWalletFeatureAvailable();
+        mUiExecutor = uiExecutor;
+        queryWalletCards();
 
-        if (mWalletEnabled) {
-            queryWalletCards(uiExecutor);
-        }
         updateWalletVisibility();
     }
 
-    private void queryWalletCards(Executor uiExecutor) {
+    private void queryWalletCards() {
+        if (!mWalletEnabled || mUiExecutor == null) {
+            return;
+        }
         GetWalletCardsRequest request =
                 new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
                         1 /* iconSizePx */, 2 /* maxCards */);
-        mQuickAccessWalletClient.getWalletCards(uiExecutor, request, mCardRetriever);
+        mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
     }
 
     private void onWalletClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 0694737..c22fec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,7 +18,10 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
 
+import android.animation.ValueAnimator;
 import android.annotation.ColorInt;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -47,6 +50,8 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -64,8 +69,11 @@
 /**
  * The header group on Keyguard.
  */
-public class KeyguardStatusBarView extends RelativeLayout
-        implements BatteryStateChangeCallback, OnUserInfoChangedListener, ConfigurationListener {
+public class KeyguardStatusBarView extends RelativeLayout implements
+        BatteryStateChangeCallback,
+        OnUserInfoChangedListener,
+        ConfigurationListener,
+        SystemStatusAnimationCallback {
 
     private static final int LAYOUT_NONE = 0;
     private static final int LAYOUT_CUTOUT = 1;
@@ -96,6 +104,8 @@
     private ViewGroup mStatusIconArea;
     private int mLayoutState = LAYOUT_NONE;
 
+    private SystemStatusAnimationScheduler mAnimationScheduler;
+
     /**
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
      */
@@ -125,6 +135,7 @@
         loadDimens();
         loadBlockList();
         mBatteryController = Dependency.get(BatteryController.class);
+        mAnimationScheduler = Dependency.get(SystemStatusAnimationScheduler.class);
     }
 
     @Override
@@ -349,6 +360,7 @@
         mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
         mIconManager.setBlockList(mBlockedIcons);
         Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+        mAnimationScheduler.addCallback(this);
         onThemeChanged();
     }
 
@@ -358,6 +370,7 @@
         Dependency.get(UserInfoController.class).removeCallback(this);
         Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
         Dependency.get(ConfigurationController.class).removeCallback(this);
+        mAnimationScheduler.removeCallback(this);
     }
 
     @Override
@@ -509,4 +522,30 @@
             mBatteryView.dump(fd, pw, args);
         }
     }
+
+    /** SystemStatusAnimationCallback */
+    @Override
+    public void onSystemChromeAnimationStart() {
+        if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT) {
+            mSystemIconsContainer.setVisibility(View.VISIBLE);
+            mSystemIconsContainer.setAlpha(0f);
+        }
+    }
+
+    @Override
+    public void onSystemChromeAnimationEnd() {
+        // Make sure the system icons are out of the way
+        if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
+            mSystemIconsContainer.setVisibility(View.INVISIBLE);
+            mSystemIconsContainer.setAlpha(0f);
+        } else {
+            mSystemIconsContainer.setAlpha(1f);
+            mSystemIconsContainer.setVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    public void onSystemChromeAnimationUpdate(ValueAnimator anim) {
+        mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5aecb72..0c8122c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -666,7 +666,9 @@
         pw.println(TAG + ":");
         pw.println("  mKeyguardDisplayMode=" + mKeyguardDisplayMode);
         pw.println(mCurrentState);
-        mNotificationShadeView.getViewRootImpl().dump("  ", pw);
+        if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) {
+            mNotificationShadeView.getViewRootImpl().dump("  ", pw);
+        }
     }
 
     @Override
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 d386ebd..43d525d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,6 +208,8 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -426,6 +428,8 @@
     private final DemoModeController mDemoModeController;
     private NotificationsController mNotificationsController;
     private final OngoingCallController mOngoingCallController;
+    private final SystemStatusAnimationScheduler mAnimationScheduler;
+    private final PrivacyDotViewController mDotViewController;
 
     // expanded notifications
     // the sliding/resizing panel within the notification window
@@ -794,6 +798,8 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             WiredChargingRippleController chargingRippleAnimationController,
             OngoingCallController ongoingCallController,
+            SystemStatusAnimationScheduler animationScheduler,
+            PrivacyDotViewController dotViewController,
             TunerService tunerService,
             FeatureFlags featureFlags) {
         super(context);
@@ -875,6 +881,8 @@
         mBrightnessSliderFactory = brightnessSliderFactory;
         mChargingRippleAnimationController = chargingRippleAnimationController;
         mOngoingCallController = ongoingCallController;
+        mAnimationScheduler = animationScheduler;
+        mDotViewController = dotViewController;
         mFeatureFlags = featureFlags;
 
         tunerService.addTunable(
@@ -1171,7 +1179,10 @@
                 }).getFragmentManager()
                 .beginTransaction()
                 .replace(R.id.status_bar_container,
-                        new CollapsedStatusBarFragment(mOngoingCallController),
+                        new CollapsedStatusBarFragment(
+                                mOngoingCallController,
+                                mAnimationScheduler,
+                                mDotViewController),
                         CollapsedStatusBarFragment.TAG)
                 .commit();
 
@@ -1788,7 +1799,15 @@
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade) {
-        startActivityDismissingKeyguard(intent, false, dismissShade);
+        startActivityDismissingKeyguard(intent, false /* onlyProvisioned */, dismissShade);
+    }
+
+    @Override
+    public void startActivity(Intent intent, boolean dismissShade,
+            ActivityLaunchAnimator.Controller animationController) {
+        startActivityDismissingKeyguard(intent, false, dismissShade,
+                false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
+                0 /* flags */, animationController);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 3404528..4356b52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -39,6 +39,7 @@
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.EventLog;
+import android.view.View;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -462,7 +463,7 @@
         mActivityStarter.dismissKeyguardThenExecute(() -> {
             AsyncTask.execute(() -> {
                 ActivityLaunchAnimator.Controller animationController = null;
-                if (!mStatusBar.isOccluded() && mStatusBar.areLaunchAnimationsEnabled()) {
+                if (mStatusBar.areLaunchAnimationsEnabled()) {
                     animationController = new StatusBarLaunchAnimatorController(
                             mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
                             true /* isActivityIntent */);
@@ -495,7 +496,7 @@
     }
 
     @Override
-    public void startHistoryIntent(boolean showHistory) {
+    public void startHistoryIntent(View view, boolean showHistory) {
         mActivityStarter.dismissKeyguardThenExecute(() -> {
             AsyncTask.execute(() -> {
                 Intent intent = showHistory ? new Intent(
@@ -506,11 +507,27 @@
                 if (showHistory) {
                     tsb.addNextIntent(intent);
                 }
-                tsb.startActivities(null, UserHandle.CURRENT);
 
-                // Putting it back on the main thread, since we're touching views
-                mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
-                        CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+                ActivityLaunchAnimator.Controller animationController = null;
+                if (mStatusBar.areLaunchAnimationsEnabled()) {
+                    animationController = new StatusBarLaunchAnimatorController(
+                            ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
+                            true /* isActivityIntent */);
+                }
+
+                mActivityLaunchAnimator.startIntentWithAnimation(animationController,
+                        (adapter) -> tsb.startActivities(
+                                getActivityOptions(mStatusBar.getDisplayId(), adapter),
+                                UserHandle.CURRENT));
+
+                // Note that other cases when we should still collapse (like activity already on
+                // top) is handled by the StatusBarLaunchAnimatorController.
+                boolean shouldCollapse = animationController == null;
+                if (shouldCollapse) {
+                    // Putting it back on the main thread, since we're touching views
+                    mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
+                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+                }
             });
             return true;
         }, null, false /* afterKeyguardGone */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 2c2779e..24e6db8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -61,6 +61,8 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -209,6 +211,8 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             WiredChargingRippleController chargingRippleAnimationController,
             OngoingCallController ongoingCallController,
+            SystemStatusAnimationScheduler animationScheduler,
+            PrivacyDotViewController dotViewController,
             TunerService tunerService,
             FeatureFlags featureFlags) {
         return new StatusBar(
@@ -293,6 +297,8 @@
                 brightnessSliderFactory,
                 chargingRippleAnimationController,
                 ongoingCallController,
+                animationScheduler,
+                dotViewController,
                 tunerService,
                 featureFlags);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index d195062..d1a2c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -222,6 +222,11 @@
         if (mIsDismissed) {
             return;
         }
+        int cardWidthPx = mCardCarousel.getCardWidthPx();
+        int cardHeightPx = mCardCarousel.getCardHeightPx();
+        if (cardWidthPx == 0 || cardHeightPx == 0) {
+            return;
+        }
         if (!mHasRegisteredListener) {
             // Listener is registered even when device is locked. Should only be registered once.
             mWalletClient.addWalletServiceEventListener(this);
@@ -231,8 +236,6 @@
         mWalletView.show();
         mWalletView.hideErrorMessage();
         int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
-        int cardWidthPx = mCardCarousel.getCardWidthPx();
-        int cardHeightPx = mCardCarousel.getCardHeightPx();
         GetWalletCardsRequest request =
                 new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
         mWalletClient.getWalletCards(mExecutor, request, this);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 74b79d5..81bb819 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -268,6 +268,7 @@
             public void onStop() {
                 mSysUiMainExecutor.execute(() -> {
                     if (oneHanded.isOneHandedEnabled()) {
+                        // Log metrics for 3-button navigation mode.
                         oneHanded.stopOneHanded(
                                 OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
                     } else if (oneHanded.isSwipeToNotificationEnabled()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 59262cf..3252750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -63,6 +63,7 @@
 import com.android.systemui.R.dimen;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
@@ -94,6 +95,8 @@
     private BroadcastDispatcher mBroadcastDispatcher;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private PrivacyDotViewController mDotViewController;
 
     @Before
     public void setup() {
@@ -116,7 +119,7 @@
         mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
 
         mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
-                mBroadcastDispatcher, mTunerService, mUserTracker) {
+                mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController) {
             @Override
             public void start() {
                 super.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 3f0831c..78c6717 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -233,9 +233,9 @@
                 TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME,
                 AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
         assertEquals(2,
-                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
-        assertEquals(1,
-                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size());
+                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID), false).size());
+        assertEquals(1, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER),
+                false).size());
     }
 
     @Test
@@ -245,11 +245,11 @@
         mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
         assertEquals(0, mController.getActiveAppOpsForUser(
-                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size());
         mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true);
         assertEquals(0, mController.getActiveAppOpsForUser(
-                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size());
     }
 
     @Test
@@ -441,7 +441,19 @@
     }
 
     @Test
-    public void testOnlyRecordAudioPaused() {
+    public void testPausedPhoneCallMicrophoneFilteredOut() {
+        mController.addCallback(new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE}, mCallback);
+        mTestableLooper.processAllMessages();
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mController.getActiveAppOps().isEmpty());
+    }
+
+    @Test
+    public void testOnlyRecordAudioPhoneCallMicrophonePaused() {
         mController.addCallback(new int[]{
                 AppOpsManager.OP_RECORD_AUDIO,
                 AppOpsManager.OP_CAMERA
@@ -532,6 +544,40 @@
     }
 
     @Test
+    public void testPhoneCallMicrophoneFilteredWhenMicDisabled() {
+        mController.addCallback(
+                new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_CAMERA},
+                mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(0).getCode());
+        assertFalse(list.get(0).isDisabled());
+
+        // Add a camera op, and disable the microphone. The camera op should be the only op returned
+        mController.onSensorBlockedChanged(MICROPHONE, true);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+
+
+        // Re enable the microphone, and verify the op returns
+        mController.onSensorBlockedChanged(MICROPHONE, false);
+        mTestableLooper.processAllMessages();
+
+        list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+        int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0;
+        assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(micIdx).getCode());
+    }
+
+    @Test
     public void testCameraFilteredWhenCameraDisabled() {
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
                 mCallback);
@@ -563,6 +609,39 @@
         assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode());
     }
 
+    @Test
+    public void testPhoneCallCameraFilteredWhenCameraDisabled() {
+        mController.addCallback(
+                new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_CAMERA},
+                mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(0).getCode());
+        assertFalse(list.get(0).isDisabled());
+
+        // Add an audio op, and disable the camera. The audio op should be the only op returned
+        mController.onSensorBlockedChanged(CAMERA, true);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
+
+        // Re enable the camera, and verify the op returns
+        mController.onSensorBlockedChanged(CAMERA, false);
+        mTestableLooper.processAllMessages();
+
+        list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+        int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_PHONE_CALL_CAMERA ? 0 : 1;
+        assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode());
+    }
+
     private class TestHandler extends AppOpsControllerImpl.H {
         TestHandler(Looper looper) {
             mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 42e88b0..63ce98a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.app.IWallpaperManager;
 import android.os.PowerManager;
 import android.testing.AndroidTestingRunner;
 
@@ -43,9 +44,12 @@
     private WakefulnessLifecycle mWakefulness;
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
 
+    private IWallpaperManager mWallpaperManager;
+
     @Before
     public void setUp() throws Exception {
-        mWakefulness = new WakefulnessLifecycle();
+        mWallpaperManager = mock(IWallpaperManager.class);
+        mWakefulness = new WakefulnessLifecycle(mContext, mWallpaperManager);
         mWakefulnessObserver = mock(WakefulnessLifecycle.Observer.class);
         mWakefulness.addObserver(mWakefulnessObserver);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 0ce03ad..81ca4c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -22,8 +22,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
@@ -35,10 +33,7 @@
 
 import android.app.INotificationManager;
 import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.app.Person;
-import android.app.people.ConversationChannel;
 import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
@@ -47,7 +42,6 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -57,7 +51,6 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.ContactsContract;
-import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
@@ -81,10 +74,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
@@ -185,8 +176,6 @@
     @Mock
     private IPeopleManager mPeopleManager;
     @Mock
-    private LauncherApps mLauncherApps;
-    @Mock
     private IAppWidgetService mIAppWidgetService;
     @Mock
     private AppWidgetManager mAppWidgetManager;
@@ -239,84 +228,6 @@
     }
 
     @Test
-    public void testGetRecentTilesReturnsSortedListWithOnlyRecentConversations() throws Exception {
-        // Ensure the less-recent Important conversation is before more recent conversations.
-        ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID_1, false, 3);
-        ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID_1 + 1, true, 3);
-        ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID_1 + 2,
-                true, 1);
-        when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
-                new ParceledListSlice(Arrays.asList(
-                        newerNonImportantConversation, newerImportantConversation,
-                        olderImportantConversation)));
-
-        // Ensure the non-Important conversation is sorted between these recent conversations.
-        ConversationChannel recentConversationBeforeNonImportantConversation =
-                getConversationChannel(
-                        SHORTCUT_ID_1 + 3, 4);
-        ConversationChannel recentConversationAfterNonImportantConversation =
-                getConversationChannel(SHORTCUT_ID_1 + 4,
-                        2);
-        when(mPeopleManager.getRecentConversations()).thenReturn(
-                new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
-                        recentConversationBeforeNonImportantConversation)));
-
-        List<String> orderedShortcutIds = PeopleSpaceUtils.getRecentTiles(
-                mContext, mNotificationManager, mPeopleManager,
-                mLauncherApps, mNotificationEntryManager)
-                .stream().map(tile -> tile.getId()).collect(Collectors.toList());
-
-        // Check for sorted recent conversations.
-        assertThat(orderedShortcutIds).containsExactly(
-                recentConversationBeforeNonImportantConversation.getShortcutInfo().getId(),
-                newerNonImportantConversation.getShortcutInfo().getId(),
-                recentConversationAfterNonImportantConversation.getShortcutInfo().getId())
-                .inOrder();
-    }
-
-    @Test
-    public void testGetPriorityTilesReturnsSortedListWithOnlyImportantConversations()
-            throws Exception {
-        // Ensure the less-recent Important conversation is before more recent conversations.
-        ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID_1, false, 3);
-        ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID_1 + 1, true, 3);
-        ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID_1 + 2,
-                true, 1);
-        when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
-                new ParceledListSlice(Arrays.asList(
-                        newerNonImportantConversation, newerImportantConversation,
-                        olderImportantConversation)));
-
-        // Ensure the non-Important conversation is sorted between these recent conversations.
-        ConversationChannel recentConversationBeforeNonImportantConversation =
-                getConversationChannel(
-                        SHORTCUT_ID_1 + 3, 4);
-        ConversationChannel recentConversationAfterNonImportantConversation =
-                getConversationChannel(SHORTCUT_ID_1 + 4,
-                        2);
-        when(mPeopleManager.getRecentConversations()).thenReturn(
-                new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
-                        recentConversationBeforeNonImportantConversation)));
-
-        List<String> orderedShortcutIds = PeopleSpaceUtils.getPriorityTiles(
-                mContext, mNotificationManager, mPeopleManager,
-                mLauncherApps, mNotificationEntryManager)
-                .stream().map(tile -> tile.getId()).collect(Collectors.toList());
-
-        // Check for sorted priority conversations.
-        assertThat(orderedShortcutIds).containsExactly(
-                newerImportantConversation.getShortcutInfo().getId(),
-                olderImportantConversation.getShortcutInfo().getId())
-                .inOrder();
-    }
-
-    @Test
     public void testGetMessagingStyleMessagesNoMessage() {
         Notification notification = new Notification.Builder(mContext, "test")
                 .setContentTitle("TEST_TITLE")
@@ -570,30 +481,4 @@
         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
                 any());
     }
-
-    private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
-            boolean importantConversation, long lastInteractionTimestamp) throws Exception {
-        ConversationChannelWrapper convo = new ConversationChannelWrapper();
-        NotificationChannel notificationChannel = new NotificationChannel(shortcutId,
-                "channel" + shortcutId,
-                NotificationManager.IMPORTANCE_DEFAULT);
-        notificationChannel.setImportantConversation(importantConversation);
-        convo.setNotificationChannel(notificationChannel);
-        convo.setShortcutInfo(new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
-                "name").build());
-        when(mPeopleManager.getLastInteraction(anyString(), anyInt(),
-                eq(shortcutId))).thenReturn(lastInteractionTimestamp);
-        return convo;
-    }
-
-    private ConversationChannel getConversationChannel(String shortcutId,
-            long lastInteractionTimestamp) throws Exception {
-        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
-                "name").build();
-        ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
-                lastInteractionTimestamp, false);
-        when(mPeopleManager.getLastInteraction(anyString(), anyInt(),
-                eq(shortcutId))).thenReturn(lastInteractionTimestamp);
-        return convo;
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 107ac83..3cc55f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -114,8 +114,6 @@
 
         when(mMockContext.getString(R.string.birthday_status)).thenReturn(
                 mContext.getString(R.string.birthday_status));
-        when(mMockContext.getString(R.string.basic_status)).thenReturn(
-                mContext.getString(R.string.basic_status));
         when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
         when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
                 mContext.getString(R.string.over_timestamp));
@@ -126,7 +124,6 @@
         when(resources.getConfiguration()).thenReturn(configuration);
         when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
         TextView textView = mock(TextView.class);
-        // when(new TextView(mMockContext)).thenReturn(textView);
         when(textView.getLineHeight()).thenReturn(16);
         when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
         mPeopleTileViewHelper = new PeopleTileViewHelper(mContext,
@@ -134,16 +131,41 @@
     }
 
     @Test
-    public void testCreateRemoteViewsWithLastInteractionTime() {
+    public void testCreateRemoteViewsWithLastInteractionTimeUnderOneDayHidden() {
         RemoteViews views = new PeopleTileViewHelper(mContext,
                 PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
         View result = views.apply(mContext, null);
 
+        // Not showing last interaction.
+        assertEquals(View.GONE, result.findViewById(R.id.last_interaction).getVisibility());
+
+        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+                getSizeInDp(R.dimen.required_width_for_large));
+        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+                getSizeInDp(R.dimen.required_height_for_large));
+        RemoteViews largeView = new PeopleTileViewHelper(mContext,
+                PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+        View largeResult = largeView.apply(mContext, null);
+
+        // Not showing last interaction.
+        assertEquals(View.GONE, largeResult.findViewById(R.id.last_interaction).getVisibility());
+    }
+
+    @Test
+    public void testCreateRemoteViewsWithLastInteractionTime() {
+        PeopleSpaceTile tileWithLastInteraction =
+                PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setLastInteractionTimestamp(
+                        123445L).build();
+        RemoteViews views = new PeopleTileViewHelper(mContext,
+                tileWithLastInteraction, 0, mOptions).getViews();
+        View result = views.apply(mContext, null);
+
         TextView name = (TextView) result.findViewById(R.id.name);
         assertEquals(name.getText(), NAME);
         // Has last interaction.
+        assertEquals(View.VISIBLE, result.findViewById(R.id.last_interaction).getVisibility());
         TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
-        assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+        assertEquals(lastInteraction.getText(), "Over 2 weeks ago");
         // No availability.
         assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
         // Shows person icon.
@@ -154,7 +176,7 @@
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_medium) - 1);
         RemoteViews smallView = new PeopleTileViewHelper(mContext,
-                PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+                tileWithLastInteraction, 0, mOptions).getViews();
         View smallResult = smallView.apply(mContext, null);
 
         // Show name over predefined icon.
@@ -171,14 +193,15 @@
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = new PeopleTileViewHelper(mContext,
-                PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+                tileWithLastInteraction, 0, mOptions).getViews();
         View largeResult = largeView.apply(mContext, null);
 
         name = (TextView) largeResult.findViewById(R.id.name);
         assertEquals(name.getText(), NAME);
         // Has last interaction.
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.last_interaction).getVisibility());
         lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
-        assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+        assertEquals(lastInteraction.getText(), "Over 2 weeks ago");
         // No availability.
         assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
         // Shows person icon.
@@ -202,8 +225,7 @@
         TextView name = (TextView) result.findViewById(R.id.name);
         assertEquals(name.getText(), NAME);
         // Has last interaction over status.
-        TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
-        assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+        assertEquals(View.GONE, result.findViewById(R.id.last_interaction).getVisibility());
         // Has availability.
         assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
         // Has person icon.
@@ -237,14 +259,13 @@
         name = (TextView) largeResult.findViewById(R.id.name);
         assertEquals(name.getText(), NAME);
         // Has last interaction.
-        lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
-        assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+        assertEquals(View.GONE, largeResult.findViewById(R.id.last_interaction).getVisibility());
         // Has availability.
-        assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
         // Shows person icon.
-        assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.person_icon).getVisibility());
         // No status.
-        assertThat((View) result.findViewById(R.id.text_content)).isNull();
+        assertThat((View) largeResult.findViewById(R.id.text_content)).isNull();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index 0ef3ca2..ccb40e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -29,6 +29,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.notification.NotificationListenerService;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -83,6 +84,8 @@
     private BubblesManager mBubblesManager;
     @Mock
     private NotificationListenerService.Ranking mRanking;
+    @Mock
+    private UserManager mUserManager;
 
     @Captor
     private ArgumentCaptor<NotificationVisibility> mNotificationVisibilityCaptor;
@@ -93,7 +96,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mActivity = new LaunchConversationActivity(mNotificationEntryManager,
-                Optional.of(mBubblesManager));
+                Optional.of(mBubblesManager), mUserManager);
         mActivity.setIsForTesting(true, mIStatusBarService);
         mIntent = new Intent();
         mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, "tile ID");
@@ -113,6 +116,7 @@
         when(mNotifEntryCanBubble.canBubble()).thenReturn(true);
         when(mNotifEntryNoRanking.getRanking()).thenReturn(null);
         when(mRanking.getRank()).thenReturn(NOTIF_RANK);
+        when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
     }
 
     @Test
@@ -176,4 +180,18 @@
                 anyInt(), any(), anyInt(), anyInt(), any());
         verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(mNotifEntryCanBubble));
     }
+
+    @Test
+    public void testQuietModeOpensQuietModeDialog() throws Exception {
+        mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY,
+                NOTIF_KEY);
+        when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE))).thenReturn(true);
+        mActivity.setIntent(mIntent);
+        mActivity.onCreate(new Bundle());
+
+        assertThat(mActivity.isFinishing()).isTrue();
+        verify(mIStatusBarService, never()).onNotificationClear(any(),
+                anyInt(), any(), anyInt(), anyInt(), any());
+        verify(mBubblesManager, never()).expandStackAndSelectBubble(any());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 7125500..e9be8d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -35,7 +35,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -45,8 +47,10 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Person;
 import android.app.people.ConversationChannel;
 import android.app.people.ConversationStatus;
@@ -59,11 +63,14 @@
 import android.content.SharedPreferences;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 
@@ -95,6 +102,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -169,6 +177,10 @@
     private NotificationEntryManager mNotificationEntryManager;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private INotificationManager mNotificationManager;
+    @Mock
+    private UserManager mUserManager;
 
     @Captor
     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -189,7 +201,8 @@
         mProvider = new PeopleSpaceWidgetProvider();
         mProvider.setPeopleSpaceWidgetManager(mManager);
         mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
-                mLauncherApps, mNotificationEntryManager, mPackageManager, true, mProvider);
+                mLauncherApps, mNotificationEntryManager, mPackageManager, true, mProvider,
+                mUserManager, mNotificationManager);
         mManager.attach(mListenerService);
 
         verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
@@ -201,6 +214,98 @@
         addTileForWidget(PERSON_TILE_WITH_SAME_URI, WIDGET_ID_WITH_SAME_URI);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
                 .thenReturn(new Bundle());
+        when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+    }
+
+    @Test
+    public void testGetRecentTilesReturnsSortedListWithOnlyRecentConversations() throws Exception {
+        // Ensure the less-recent Important conversation is before more recent conversations.
+        ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID, false, 3);
+        ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID + 1, true, 3);
+        ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID + 2,
+                true, 1);
+        when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
+                new ParceledListSlice(Arrays.asList(
+                        newerNonImportantConversation, newerImportantConversation,
+                        olderImportantConversation)));
+
+        // Ensure the non-Important conversation is sorted between these recent conversations.
+        ConversationChannel recentConversationBeforeNonImportantConversation =
+                getConversationChannel(
+                        SHORTCUT_ID + 3, 4);
+        ConversationChannel recentConversationAfterNonImportantConversation =
+                getConversationChannel(SHORTCUT_ID + 4,
+                        2);
+        when(mIPeopleManager.getRecentConversations()).thenReturn(
+                new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
+                        recentConversationBeforeNonImportantConversation)));
+
+        List<String> orderedShortcutIds = mManager.getRecentTiles()
+                .stream().map(tile -> tile.getId()).collect(Collectors.toList());
+
+        // Check for sorted recent conversations.
+        assertThat(orderedShortcutIds).containsExactly(
+                recentConversationBeforeNonImportantConversation.getShortcutInfo().getId(),
+                newerNonImportantConversation.getShortcutInfo().getId(),
+                recentConversationAfterNonImportantConversation.getShortcutInfo().getId())
+                .inOrder();
+    }
+
+    @Test
+    public void testGetPriorityTilesReturnsSortedListWithOnlyImportantConversations()
+            throws Exception {
+        // Ensure the less-recent Important conversation is before more recent conversations.
+        ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID, false, 3);
+        ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID + 1, true, 3);
+        ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID + 2,
+                true, 1);
+        when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
+                new ParceledListSlice(Arrays.asList(
+                        newerNonImportantConversation, newerImportantConversation,
+                        olderImportantConversation)));
+
+        List<String> orderedShortcutIds = mManager.getPriorityTiles()
+                .stream().map(tile -> tile.getId()).collect(Collectors.toList());
+
+        // Check for sorted priority conversations.
+        assertThat(orderedShortcutIds).containsExactly(
+                newerImportantConversation.getShortcutInfo().getId(),
+                olderImportantConversation.getShortcutInfo().getId())
+                .inOrder();
+    }
+
+    @Test
+    public void testGetTilesReturnsNothingInQuietMode()
+            throws Exception {
+        // Ensure the less-recent Important conversation is before more recent conversations.
+        ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID, false, 3);
+        ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID + 1, true, 3);
+        ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
+                SHORTCUT_ID + 2,
+                true, 1);
+        when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
+                new ParceledListSlice(Arrays.asList(
+                        newerNonImportantConversation, newerImportantConversation,
+                        olderImportantConversation)));
+        ConversationChannel recentConversation =
+                getConversationChannel(
+                        SHORTCUT_ID + 3, 4);
+        when(mIPeopleManager.getRecentConversations()).thenReturn(
+                new ParceledListSlice(Arrays.asList(recentConversation)));
+
+        when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+        // Check nothing returned.
+        assertThat(mManager.getPriorityTiles()).isEmpty();
+        assertThat(mManager.getRecentTiles()).isEmpty();
     }
 
     @Test
@@ -1208,4 +1313,30 @@
         editor.putStringSet(contactUri.toString(), storedWidgetIdsByUri);
         editor.apply();
     }
+
+    private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
+            boolean importantConversation, long lastInteractionTimestamp) throws Exception {
+        ConversationChannelWrapper convo = new ConversationChannelWrapper();
+        NotificationChannel notificationChannel = new NotificationChannel(shortcutId,
+                "channel" + shortcutId,
+                NotificationManager.IMPORTANCE_DEFAULT);
+        notificationChannel.setImportantConversation(importantConversation);
+        convo.setNotificationChannel(notificationChannel);
+        convo.setShortcutInfo(new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+                "name").build());
+        when(mIPeopleManager.getLastInteraction(anyString(), anyInt(),
+                eq(shortcutId))).thenReturn(lastInteractionTimestamp);
+        return convo;
+    }
+
+    private ConversationChannel getConversationChannel(String shortcutId,
+            long lastInteractionTimestamp) throws Exception {
+        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+                "name").build();
+        ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+                lastInteractionTimestamp, false);
+        when(mIPeopleManager.getLastInteraction(anyString(), anyInt(),
+                eq(shortcutId))).thenReturn(lastInteractionTimestamp);
+        return convo;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 791dd12..05a1e4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -28,6 +28,7 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.settings.UserTracker
@@ -43,6 +44,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Captor
@@ -86,6 +88,8 @@
     private lateinit var privacyLogger: PrivacyLogger
     @Mock
     private lateinit var keyguardStateController: KeyguardStateController
+    @Mock
+    private lateinit var appOpsController: AppOpsController
     @Captor
     private lateinit var dialogDismissedCaptor: ArgumentCaptor<PrivacyDialog.OnDialogDismissed>
     @Captor
@@ -131,6 +135,7 @@
                 uiExecutor,
                 privacyLogger,
                 keyguardStateController,
+                appOpsController,
                 dialogProvider
         )
     }
@@ -143,18 +148,27 @@
     }
 
     @Test
+    fun testMicMutedParameter() {
+        `when`(appOpsController.isMicMuted).thenReturn(true)
+        controller.showDialog(context)
+        backgroundExecutor.runAllReady()
+
+        verify(permissionManager).getIndicatorAppOpUsageData(true)
+    }
+
+    @Test
     fun testPermissionManagerOnlyCalledInBackgroundThread() {
         controller.showDialog(context)
-        verify(permissionManager, never()).indicatorAppOpUsageData
+        verify(permissionManager, never()).getIndicatorAppOpUsageData(anyBoolean())
         backgroundExecutor.runAllReady()
-        verify(permissionManager).indicatorAppOpUsageData
+        verify(permissionManager).getIndicatorAppOpUsageData(anyBoolean())
     }
 
     @Test
     fun testPackageManagerOnlyCalledInBackgroundThread() {
         val usage = createMockPermGroupUsage()
         `when`(usage.isPhoneCall).thenReturn(false)
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage))
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
 
         controller.showDialog(context)
         verify(packageManager, never()).getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
@@ -217,7 +231,7 @@
                 isPhoneCall = false,
                 attribution = TEST_ATTRIBUTION
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage))
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
 
         controller.showDialog(context)
         exhaustExecutors()
@@ -246,7 +260,7 @@
                 packageName = "${TEST_PACKAGE_NAME}_microphone",
                 permGroupName = PERM_MICROPHONE
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_microphone, usage_camera)
         )
 
@@ -269,7 +283,7 @@
                 packageName = "${TEST_PACKAGE_NAME}_recent",
                 isActive = false
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_recent, usage_active)
         )
 
@@ -292,7 +306,7 @@
                 isActive = true,
                 lastAccess = 1L
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_active, usage_active_moreRecent)
         )
         controller.showDialog(context)
@@ -319,7 +333,7 @@
                 isActive = false,
                 lastAccess = 2L
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_recent, usage_mostRecent, usage_moreRecent)
         )
 
@@ -342,7 +356,7 @@
                 permGroupName = PERM_LOCATION
         )
 
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_camera, usage_location, usage_microphone)
         )
         `when`(privacyItemController.micCameraAvailable).thenReturn(false)
@@ -366,7 +380,7 @@
                 permGroupName = PERM_LOCATION
         )
 
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_camera, usage_location, usage_microphone)
         )
         `when`(privacyItemController.locationAvailable).thenReturn(false)
@@ -392,7 +406,7 @@
                 permGroupName = PERM_LOCATION
         )
 
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_camera, usage_location, usage_microphone)
         )
         `when`(privacyItemController.micCameraAvailable).thenReturn(true)
@@ -416,7 +430,7 @@
                 permGroupName = PERM_LOCATION
         )
 
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_camera, usage_location, usage_microphone)
         )
         `when`(privacyItemController.micCameraAvailable).thenReturn(false)
@@ -433,7 +447,8 @@
         val usage_enterprise = createMockPermGroupUsage(
                 uid = generateUidForUser(ENT_USER_ID)
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage_enterprise))
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+                .thenReturn(listOf(usage_enterprise))
 
         controller.showDialog(context)
         exhaustExecutors()
@@ -446,7 +461,8 @@
         val usage_other = createMockPermGroupUsage(
                 uid = generateUidForUser(ENT_USER_ID + 1)
         )
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage_other))
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+                .thenReturn(listOf(usage_other))
 
         controller.showDialog(context)
         exhaustExecutors()
@@ -514,7 +530,8 @@
     }
 
     private fun setUpDefaultMockResponses() {
-        `when`(permissionManager.indicatorAppOpUsageData).thenReturn(emptyList())
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(emptyList())
+        `when`(appOpsController.isMicMuted).thenReturn(false)
 
         `when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
                 .thenAnswer {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index bba1c6a..e4d7b1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -46,7 +46,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyList
 import org.mockito.Captor
 import org.mockito.Mock
@@ -156,7 +156,7 @@
     fun testDistinctItems() {
         doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+                .`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.addCallback(callback)
         executor.runAllReady()
@@ -168,7 +168,7 @@
     fun testSimilarItemsDifferentTimeStamp() {
         doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+                .`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.addCallback(callback)
         executor.runAllReady()
@@ -215,7 +215,7 @@
 
     @Test
     fun testMultipleCallbacksAreUpdated() {
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         val otherCallback = mock(PrivacyItemController.Callback::class.java)
         privacyItemController.addCallback(callback)
@@ -233,7 +233,7 @@
 
     @Test
     fun testRemoveCallback() {
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
         val otherCallback = mock(PrivacyItemController.Callback::class.java)
         privacyItemController.addCallback(callback)
         privacyItemController.addCallback(otherCallback)
@@ -254,7 +254,7 @@
     fun testListShouldNotHaveNull() {
         doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
                         AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+                .`when`(appOpsController).getActiveAppOps(anyBoolean())
         privacyItemController.addCallback(callback)
         executor.runAllReady()
         executor.runAllReady()
@@ -292,7 +292,7 @@
 
         doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
                 AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+                .`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.addCallback(callback)
         executor.runAllReady()
@@ -306,7 +306,7 @@
     @Test
     fun testNotUpdated_LocationChangeWhenOnlyMicCamera() {
         doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+                .`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.addCallback(callback)
         changeLocation(false)
@@ -338,7 +338,7 @@
     fun testLogListUpdated() {
         doReturn(listOf(
                 AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0))
-        ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        ).`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.addCallback(callback)
         executor.runAllReady()
@@ -362,10 +362,10 @@
     }
 
     @Test
-    fun testListRequestedForAllUsers() {
+    fun testListRequestedShowPaused() {
         privacyItemController.addCallback(callback)
         executor.runAllReady()
-        verify(appOpsController).getActiveAppOpsForUser(UserHandle.USER_ALL)
+        verify(appOpsController).getActiveAppOps(true)
     }
 
     @Test
@@ -377,7 +377,7 @@
         doReturn(listOf(
                 AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
                 AppOpItem(AppOpsManager.OP_CAMERA, otherUserUid, TEST_PACKAGE_NAME, 0))
-        ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        ).`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
         executor.runAllReady()
@@ -401,7 +401,7 @@
                 AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
                 AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0),
                 AppOpItem(AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0))
-        ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        ).`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
         executor.runAllReady()
@@ -424,7 +424,7 @@
                 AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
                 AppOpItem(AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, 0))
-        ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        ).`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
         executor.runAllReady()
@@ -442,7 +442,7 @@
     fun testPassageOfTimeDoesNotRemoveIndicators() {
         doReturn(listOf(
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
-        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        )).`when`(appOpsController).getActiveAppOps(anyBoolean())
 
         privacyItemController.addCallback(callback)
 
@@ -458,12 +458,12 @@
         // Start with some element at time 0
         doReturn(listOf(
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
-        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        )).`when`(appOpsController).getActiveAppOps(anyBoolean())
         privacyItemController.addCallback(callback)
         executor.runAllReady()
 
         // Then remove it at time HOLD + 1
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
         fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS + 1)
 
         verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
@@ -481,12 +481,12 @@
         // Start with some element at time 0
         doReturn(listOf(
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
-        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        )).`when`(appOpsController).getActiveAppOps(anyBoolean())
         privacyItemController.addCallback(callback)
         executor.runAllReady()
 
         // Then remove it at time HOLD - 1
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
         fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
 
         verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
@@ -504,12 +504,12 @@
         // Start with some element at time 0
         doReturn(listOf(
                 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
-        )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        )).`when`(appOpsController).getActiveAppOps(anyBoolean())
         privacyItemController.addCallback(callback)
         executor.runAllReady()
 
         // Then remove it at time HOLD - 1
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
         fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
 
         verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
@@ -525,6 +525,30 @@
         assertTrue(privacyItemController.privacyList.isEmpty())
     }
 
+    @Test
+    fun testPausedElementsAreRemoved() {
+        val item = AppOpItem(
+                AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID,
+                TEST_PACKAGE_NAME,
+                elapsedTime
+        )
+        `when`(appOpsController.getActiveAppOps(anyBoolean())).thenReturn(listOf(item))
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+
+        item.isDisabled = true
+        fakeClock.advanceTime(1)
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+
+        executor.runAllReady()
+
+        verify(callback).onPrivacyItemsChanged(emptyList())
+        assertTrue(privacyItemController.privacyList.isEmpty())
+    }
+
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
     private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index d236023..4bba0d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -233,6 +233,11 @@
     }
 
     @Test
+    public void testGetTileLabel() {
+        assertEquals(mContext.getString(R.string.wallet_title), mTile.getTileLabel().toString());
+    }
+
+    @Test
     public void testHandleUpdateState_hasCard_deviceLocked_tileInactive() {
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
         QSTile.State state = new QSTile.State();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 67fd5eb..929a7e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -37,6 +37,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 
@@ -58,6 +60,8 @@
     private View mCenteredNotificationAreaView;
     private StatusBarStateController mStatusBarStateController;
     private OngoingCallController mOngoingCallController;
+    private SystemStatusAnimationScheduler mAnimationScheduler;
+    private PrivacyDotViewController mDotViewController;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -212,6 +216,11 @@
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
         mOngoingCallController = mock(OngoingCallController.class);
-        return new CollapsedStatusBarFragment(mOngoingCallController);
+        mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
+        mDotViewController = mock(PrivacyDotViewController.class);
+        return new CollapsedStatusBarFragment(
+                mOngoingCallController,
+                mAnimationScheduler,
+                mDotViewController);
     }
 }
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 08d6d2d..11f96c8 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
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.IWallpaperManager;
 import android.app.Notification;
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
@@ -113,6 +114,8 @@
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -263,8 +266,11 @@
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
     @Mock private WiredChargingRippleController mWiredChargingRippleController;
     @Mock private OngoingCallController mOngoingCallController;
+    @Mock private SystemStatusAnimationScheduler mAnimationScheduler;
+    @Mock private PrivacyDotViewController mDotViewController;
     @Mock private TunerService mTunerService;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private IWallpaperManager mWallpaperManager;
     private ShadeController mShadeController;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InitController mInitController = new InitController();
@@ -323,7 +329,8 @@
 
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
-        WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
+        WakefulnessLifecycle wakefulnessLifecycle =
+                new WakefulnessLifecycle(mContext, mWallpaperManager);
         wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         wakefulnessLifecycle.dispatchFinishedWakingUp();
 
@@ -429,6 +436,8 @@
                 mBrightnessSliderFactory,
                 mWiredChargingRippleController,
                 mOngoingCallController,
+                mAnimationScheduler,
+                mDotViewController,
                 mTunerService,
                 mFeatureFlags);
         when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 4471778..40439a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -36,7 +36,6 @@
 
 import android.app.Instrumentation;
 import android.net.ConnectivityManager;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
@@ -323,34 +322,37 @@
 
     public void setConnectivityViaCallbackInNetworkControllerForVcn(
             int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) {
-        mNetCapabilities.setTransportInfo(info);
-        setConnectivityCommon(networkType, validated, isConnected);
-        mDefaultCallbackInNetworkController.onCapabilitiesChanged(
-                mNetwork, new NetworkCapabilities(mNetCapabilities));
+        final NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder(mNetCapabilities);
+        builder.setTransportInfo(info);
+        setConnectivityCommon(builder, networkType, validated, isConnected);
+        mDefaultCallbackInNetworkController.onCapabilitiesChanged(mNetwork, builder.build());
     }
 
     public void setConnectivityViaCallbackInNetworkController(
             int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+        final NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder(mNetCapabilities);
         if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
-            mNetCapabilities.setTransportInfo(wifiInfo);
+            builder.setTransportInfo(wifiInfo);
         }
-        setConnectivityCommon(networkType, validated, isConnected);
-        mDefaultCallbackInNetworkController.onCapabilitiesChanged(
-                mNetwork, new NetworkCapabilities(mNetCapabilities));
+        setConnectivityCommon(builder, networkType, validated, isConnected);
+        mDefaultCallbackInNetworkController.onCapabilitiesChanged(mNetwork, builder.build());
     }
 
     public void setConnectivityViaCallbackInWifiTracker(
             int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+        final NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder(mNetCapabilities);
         if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
-            mNetCapabilities.setTransportInfo(wifiInfo);
+            builder.setTransportInfo(wifiInfo);
         }
-        setConnectivityCommon(networkType, validated, isConnected);
+        setConnectivityCommon(builder, networkType, validated, isConnected);
         if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
             if (isConnected) {
-                mNetworkCallback.onAvailable(mNetwork,
-                        new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
-                mNetworkCallback.onCapabilitiesChanged(
-                        mNetwork, new NetworkCapabilities(mNetCapabilities));
+                final NetworkCapabilities newCap = builder.build();
+                mNetworkCallback.onAvailable(mNetwork);
+                mNetworkCallback.onCapabilitiesChanged(mNetwork, newCap);
             } else {
                 mNetworkCallback.onLost(mNetwork);
             }
@@ -359,16 +361,16 @@
 
     public void setConnectivityViaCallbackInWifiTrackerForVcn(
             int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) {
-        mNetCapabilities.setTransportInfo(info);
-        setConnectivityCommon(networkType, validated, isConnected);
+        final NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder(mNetCapabilities);
+        builder.setTransportInfo(info);
+        setConnectivityCommon(builder, networkType, validated, isConnected);
         if (networkType == NetworkCapabilities.TRANSPORT_CELLULAR) {
             if (isConnected) {
-                mNetworkCallback.onAvailable(mNetwork,
-                        new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
-                mNetworkCallback.onCapabilitiesChanged(
-                        mNetwork, new NetworkCapabilities(mNetCapabilities));
-                mDefaultCallbackInWifiTracker.onCapabilitiesChanged(
-                        mNetwork, new NetworkCapabilities(mNetCapabilities));
+                final NetworkCapabilities newCap = builder.build();
+                mNetworkCallback.onAvailable(mNetwork);
+                mNetworkCallback.onCapabilitiesChanged(mNetwork, newCap);
+                mDefaultCallbackInWifiTracker.onCapabilitiesChanged(mNetwork, newCap);
             } else {
                 mNetworkCallback.onLost(mNetwork);
             }
@@ -377,26 +379,28 @@
 
     public void setConnectivityViaDefaultCallbackInWifiTracker(
             int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+        final NetworkCapabilities.Builder builder =
+                new NetworkCapabilities.Builder(mNetCapabilities);
         if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
-            mNetCapabilities.setTransportInfo(wifiInfo);
+            builder.setTransportInfo(wifiInfo);
         }
-        setConnectivityCommon(networkType, validated, isConnected);
+        setConnectivityCommon(builder, networkType, validated, isConnected);
         mDefaultCallbackInWifiTracker.onCapabilitiesChanged(
-                mNetwork, new NetworkCapabilities(mNetCapabilities));
+                mNetwork, builder.build());
     }
 
-    private void setConnectivityCommon(
+    private static void setConnectivityCommon(NetworkCapabilities.Builder builder,
         int networkType, boolean validated, boolean isConnected){
         // TODO: Separate out into several NetworkCapabilities.
         if (isConnected) {
-            mNetCapabilities.addTransportType(networkType);
+            builder.addTransportType(networkType);
         } else {
-            mNetCapabilities.removeTransportType(networkType);
+            builder.removeTransportType(networkType);
         }
         if (validated) {
-            mNetCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
         } else {
-            mNetCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index ed87a40..c38a547 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -202,8 +202,8 @@
     @Test
     public void testNetworkRequest() {
         verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
-                (NetworkRequest request) -> request.networkCapabilities.getUids() == null
-                        && request.networkCapabilities.getCapabilities().length == 0
+                (NetworkRequest request) ->
+                        request.equals(new NetworkRequest.Builder().clearCapabilities().build())
                 ), any(NetworkCallback.class));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 653946e..6f6ef72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -71,15 +71,17 @@
 public class WalletScreenControllerTest extends SysuiTestCase {
 
     private static final int MAX_CARDS = 10;
+    private static final int CARD_CAROUSEL_WIDTH = 10;
     private static final String CARD_ID = "card_id";
     private static final CharSequence SHORTCUT_SHORT_LABEL = "View all";
     private static final CharSequence SHORTCUT_LONG_LABEL = "Add a payment method";
     private static final CharSequence SERVICE_LABEL = "Wallet app";
-    private final WalletView mWalletView = new WalletView(mContext);
     private final Drawable mWalletLogo = mContext.getDrawable(android.R.drawable.ic_lock_lock);
     private final Intent mWalletIntent = new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
             .setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
 
+    private WalletView mWalletView;
+
     @Mock
     QuickAccessWalletClient mWalletClient;
     @Mock
@@ -104,6 +106,8 @@
         MockitoAnnotations.initMocks(this);
         mTestableLooper = TestableLooper.get(this);
         when(mUserTracker.getUserContext()).thenReturn(mContext);
+        mWalletView = new WalletView(mContext);
+        mWalletView.getCardCarousel().setExpectedViewWidth(CARD_CAROUSEL_WIDTH);
         when(mWalletClient.getLogo()).thenReturn(mWalletLogo);
         when(mWalletClient.getShortcutLongLabel()).thenReturn(SHORTCUT_LONG_LABEL);
         when(mWalletClient.getShortcutShortLabel()).thenReturn(SHORTCUT_SHORT_LABEL);
@@ -132,7 +136,12 @@
 
         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
 
-        mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+        QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+                mCallbackCaptor.getValue();
+
+        assertEquals(mController, callback);
+
+        callback.onWalletCardsRetrieved(response);
         mTestableLooper.processAllMessages();
 
         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4d96162..16e6671 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -630,7 +630,9 @@
     }
 
     private static IDnsResolver getDnsResolver(Context context) {
-        return IDnsResolver.Stub.asInterface(DnsResolverServiceManager.getService(context));
+        final DnsResolverServiceManager dsm = context.getSystemService(
+                DnsResolverServiceManager.class);
+        return IDnsResolver.Stub.asInterface(dsm.getService());
     }
 
     /** Handler thread used for all of the handlers below. */
@@ -1924,7 +1926,7 @@
         newNc.setAdministratorUids(new int[0]);
         if (!checkAnyPermissionOf(
                 callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
-            newNc.setSubIds(Collections.emptySet());
+            newNc.setSubscriptionIds(Collections.emptySet());
         }
 
         return newNc;
@@ -3969,17 +3971,16 @@
                 // multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
                 // is important so as to not evaluate lower priority requests further in
                 // nri.mRequests.
-                final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId)
-                        // Note that this catches two important cases:
-                        // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
-                        //    is currently satisfying the request.  This is desirable when
-                        //    cellular ends up validating but WiFi does not.
-                        // 2. Unvalidated WiFi will not be reaped when validated cellular
-                        //    is currently satisfying the request.  This is desirable when
-                        //    WiFi ends up validating and out scoring cellular.
-                        || nri.getSatisfier().getCurrentScore()
-                        < candidate.getCurrentScoreAsValidated();
-                return isNetworkNeeded;
+                final NetworkAgentInfo champion = req.equals(nri.getActiveRequest())
+                        ? nri.getSatisfier() : null;
+                // Note that this catches two important cases:
+                // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+                //    is currently satisfying the request.  This is desirable when
+                //    cellular ends up validating but WiFi does not.
+                // 2. Unvalidated WiFi will not be reaped when validated cellular
+                //    is currently satisfying the request.  This is desirable when
+                //    WiFi ends up validating and out scoring cellular.
+                return mNetworkRanker.mightBeat(req, champion, candidate.getValidatedScoreable());
             }
         }
 
@@ -5726,7 +5727,7 @@
         }
         mAppOpsManager.checkPackage(callerUid, callerPackageName);
 
-        if (!nc.getSubIds().isEmpty()) {
+        if (!nc.getSubscriptionIds().isEmpty()) {
             enforceNetworkFactoryPermission();
         }
     }
@@ -6148,6 +6149,7 @@
     @Override
     public int registerNetworkProvider(Messenger messenger, String name) {
         enforceNetworkFactoryOrSettingsPermission();
+        Objects.requireNonNull(messenger, "messenger must be non-null");
         NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
                 nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
@@ -7811,7 +7813,7 @@
             @NonNull final Collection<NetworkRequestInfo> networkRequests) {
         final NetworkReassignment changes = new NetworkReassignment();
 
-        // Gather the list of all relevant agents and sort them by score.
+        // Gather the list of all relevant agents.
         final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
         for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
             if (!nai.everConnected) {
@@ -9186,6 +9188,7 @@
     @Override
     public void unregisterConnectivityDiagnosticsCallback(
             @NonNull IConnectivityDiagnosticsCallback callback) {
+        Objects.requireNonNull(callback, "callback must be non-null");
         mConnectivityDiagnosticsHandler.sendMessage(
                 mConnectivityDiagnosticsHandler.obtainMessage(
                         ConnectivityDiagnosticsHandler
@@ -9556,6 +9559,7 @@
      */
     @Override
     public void unregisterQosCallback(@NonNull final IQosCallback callback) {
+        Objects.requireNonNull(callback, "callback must be non-null");
         mQosCallbackTracker.unregisterCallback(callback);
     }
 
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 039f4d9..4b52057 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -729,7 +729,7 @@
 
         // If multiple subscription IDs exist, they MUST all point to the same subscription
         // group. Otherwise undefined behavior may occur.
-        for (int subId : networkCapabilities.getSubIds()) {
+        for (int subId : networkCapabilities.getSubscriptionIds()) {
             // Verify that all subscriptions point to the same group
             if (subGrp != null && !subGrp.equals(snapshot.getGroupForSubId(subId))) {
                 Slog.wtf(TAG, "Got multiple subscription groups for a single network");
@@ -1003,14 +1003,14 @@
         }
 
         private boolean requiresRestartForCarrierWifi(NetworkCapabilities caps) {
-            if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubIds() == null) {
+            if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubscriptionIds() == null) {
                 return false;
             }
 
             synchronized (mCaps) {
                 for (NetworkCapabilities existing : mCaps.values()) {
                     if (existing.hasTransport(TRANSPORT_WIFI)
-                            && caps.getSubIds().equals(existing.getSubIds())) {
+                            && caps.getSubscriptionIds().equals(existing.getSubscriptionIds())) {
                         // Restart if any immutable capabilities have changed
                         return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
                                 != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED);
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/services/core/java/com/android/server/connectivity/FullScore.java
index 52da566..14cec09 100644
--- a/services/core/java/com/android/server/connectivity/FullScore.java
+++ b/services/core/java/com/android/server/connectivity/FullScore.java
@@ -259,6 +259,14 @@
     }
 
     /**
+     * Returns this score but validated.
+     */
+    public FullScore asValidated() {
+        return new FullScore(mLegacyInt, mPolicies | (1L << POLICY_IS_VALIDATED),
+                mKeepConnectedReason);
+    }
+
+    /**
      * For backward compatibility, get the legacy int.
      * This will be removed before S is published.
      */
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 584174e..18becd4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -950,6 +950,26 @@
     }
 
     /**
+     * Returns a Scoreable identical to this NAI, but validated.
+     *
+     * This is useful to probe what scoring would be if this network validated, to know
+     * whether to provisionally keep a network that may or may not validate.
+     *
+     * @return a Scoreable identical to this NAI, but validated.
+     */
+    public NetworkRanker.Scoreable getValidatedScoreable() {
+        return new NetworkRanker.Scoreable() {
+            @Override public FullScore getScore() {
+                return mScore.asValidated();
+            }
+
+            @Override public NetworkCapabilities getCapsNoCopy() {
+                return networkCapabilities;
+            }
+        };
+    }
+
+    /**
      * Return a {@link NetworkStateSnapshot} for this network.
      */
     @NonNull
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index 2b345e5..346af44 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -234,16 +234,17 @@
         NetworkAgentInfo bestNetwork = null;
         int bestScore = Integer.MIN_VALUE;
         for (final NetworkAgentInfo nai : nais) {
-            if (nai.getCurrentScore() > bestScore) {
+            final int naiScore = nai.getCurrentScore();
+            if (naiScore > bestScore) {
                 bestNetwork = nai;
-                bestScore = nai.getCurrentScore();
+                bestScore = naiScore;
             }
         }
         return bestNetwork;
     }
 
     /**
-     * Returns whether an offer has a chance to beat a champion network for a request.
+     * Returns whether a {@link Scoreable} has a chance to beat a champion network for a request.
      *
      * Offers are sent by network providers when they think they might be able to make a network
      * with the characteristics contained in the offer. If the offer has no chance to beat
@@ -257,15 +258,15 @@
      *
      * @param request The request to evaluate against.
      * @param champion The currently best network for this request.
-     * @param offer The offer.
+     * @param contestant The offer.
      * @return Whether the offer stands a chance to beat the champion.
      */
     public boolean mightBeat(@NonNull final NetworkRequest request,
             @Nullable final NetworkAgentInfo champion,
-            @NonNull final NetworkOffer offer) {
+            @NonNull final Scoreable contestant) {
         // If this network can't even satisfy the request then it can't beat anything, not
         // even an absence of network. It can't satisfy it anyway.
-        if (!request.canBeSatisfiedBy(offer.caps)) return false;
+        if (!request.canBeSatisfiedBy(contestant.getCapsNoCopy())) return false;
         // If there is no satisfying network, then this network can beat, because some network
         // is always better than no network.
         if (null == champion) return true;
@@ -274,25 +275,24 @@
             // Otherwise rank them.
             final ArrayList<Scoreable> candidates = new ArrayList<>();
             candidates.add(champion);
-            candidates.add(offer);
-            return offer == getBestNetworkByPolicy(candidates, champion);
+            candidates.add(contestant);
+            return contestant == getBestNetworkByPolicy(candidates, champion);
         } else {
-            return mightBeatByLegacyInt(request, champion.getScore(), offer);
+            return mightBeatByLegacyInt(champion.getScore(), contestant);
         }
     }
 
     /**
-     * Returns whether an offer might beat a champion according to the legacy int.
+     * Returns whether a contestant might beat a champion according to the legacy int.
      */
-    public boolean mightBeatByLegacyInt(@NonNull final NetworkRequest request,
-            @Nullable final FullScore championScore,
-            @NonNull final NetworkOffer offer) {
+    private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
+            @NonNull final Scoreable contestant) {
         final int offerIntScore;
-        if (offer.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+        if (contestant.getCapsNoCopy().hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
             // If the offer might have Internet access, then it might validate.
-            offerIntScore = offer.score.getLegacyIntAsValidated();
+            offerIntScore = contestant.getScore().getLegacyIntAsValidated();
         } else {
-            offerIntScore = offer.score.getLegacyInt();
+            offerIntScore = contestant.getScore().getLegacyInt();
         }
         return championScore.getLegacyInt() < offerIntScore;
     }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7994fcc..94a5099 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3177,7 +3177,7 @@
 
     @BinderThread
     @Override
-    public void reportPerceptible(IBinder windowToken, boolean perceptible) {
+    public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
         Objects.requireNonNull(windowToken, "windowToken must not be null");
         int uid = Binder.getCallingUid();
         synchronized (mMethodMap) {
@@ -5992,9 +5992,8 @@
 
         @BinderThread
         @Override
-        public void reportStartInput(IBinder startInputToken, IVoidResultCallback resultCallback) {
-            CallbackUtils.onResult(resultCallback,
-                    () -> mImms.reportStartInput(mToken, startInputToken));
+        public void reportStartInputAsync(IBinder startInputToken) {
+            mImms.reportStartInput(mToken, startInputToken);
         }
 
         @BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6244743..885093d 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1840,7 +1840,7 @@
 
         @BinderThread
         @Override
-        public void reportPerceptible(IBinder windowClient, boolean perceptible) {
+        public void reportPerceptibleAsync(IBinder windowClient, boolean perceptible) {
             reportNotSupported();
         }
 
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 045e06d0..2ffc62a 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -178,8 +178,9 @@
     }
 
     /** Logs that a provider has entered or exited stationary throttling. */
-    public void logProviderStationaryThrottled(String provider, boolean throttled) {
-        addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled);
+    public void logProviderStationaryThrottled(String provider, boolean throttled,
+            ProviderRequest request) {
+        addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled, request);
     }
 
     /** Logs that the location power save mode has changed. */
@@ -217,7 +218,7 @@
                         (Integer) args[1], (CallerIdentity) args[2]);
             case EVENT_PROVIDER_STATIONARY_THROTTLED:
                 return new ProviderStationaryThrottledEvent(timeDelta, (String) args[0],
-                        (Boolean) args[1]);
+                        (Boolean) args[1], (ProviderRequest) args[2]);
             case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
                 return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
             default:
@@ -355,17 +356,19 @@
     private static final class ProviderStationaryThrottledEvent extends ProviderEvent {
 
         private final boolean mStationaryThrottled;
+        private final ProviderRequest mRequest;
 
         ProviderStationaryThrottledEvent(long timeDelta, String provider,
-                boolean stationaryThrottled) {
+                boolean stationaryThrottled, ProviderRequest request) {
             super(timeDelta, provider);
             mStationaryThrottled = stationaryThrottled;
+            mRequest = request;
         }
 
         @Override
         public String getLogString() {
             return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled"
-                    : "unthrottled");
+                    : "unthrottled") + ", request = " + mRequest;
         }
     }
 
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index ab7e526..22a675a 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -206,7 +206,7 @@
                 if (D) {
                     Log.d(TAG, mName + " provider stationary throttled");
                 }
-                EVENT_LOG.logProviderStationaryThrottled(mName, true);
+                EVENT_LOG.logProviderStationaryThrottled(mName, true, mOutgoingRequest);
             }
 
             if (mDeliverLastLocationCallback != null) {
@@ -224,7 +224,7 @@
             }
         } else {
             if (oldThrottlingIntervalMs != INTERVAL_DISABLED) {
-                EVENT_LOG.logProviderStationaryThrottled(mName, false);
+                EVENT_LOG.logProviderStationaryThrottled(mName, false, mOutgoingRequest);
                 if (D) {
                     Log.d(TAG, mName + " provider stationary unthrottled");
                 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 59f00a2..6cded50 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1261,7 +1261,8 @@
         return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE;
     }
 
-    private void setKeystorePassword(byte[] password, int userHandle) {
+    @VisibleForTesting /** Note: this method is overridden in unit tests */
+    void setKeystorePassword(byte[] password, int userHandle) {
         AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
     }
 
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 48ccad33..8818023 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -154,14 +154,14 @@
      * Builds the Route selection request
      *
      * <p>This request is guaranteed to select carrier-owned, non-VCN underlying networks by virtue
-     * of a populated set of subIds as expressed in NetworkCapabilities#getSubIds(). Only carrier
-     * owned networks may be selected, as the request specifies only subIds in the VCN's
+     * of a populated set of subIds as expressed in NetworkCapabilities#getSubscriptionIds(). Only
+     * carrier owned networks may be selected, as the request specifies only subIds in the VCN's
      * subscription group, while the VCN networks are excluded by virtue of not having subIds set on
      * the VCN-exposed networks.
      */
     private NetworkRequest getRouteSelectionRequest() {
         return getBaseNetworkRequestBuilder()
-                .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
                 .build();
     }
 
@@ -177,7 +177,7 @@
     private NetworkRequest getWifiNetworkRequest() {
         return getBaseNetworkRequestBuilder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
                 .build();
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 89b7bbd..9acbdcc 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2378,6 +2378,46 @@
         }
     }
 
+    /**
+     * Propagate a wake event to the wallpaper engine.
+     */
+    public void notifyWakingUp(int x, int y, @NonNull Bundle extras) {
+        synchronized (mLock) {
+            final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+            data.connection.forEachDisplayConnector(
+                    displayConnector -> {
+                        if (displayConnector.mEngine != null) {
+                            try {
+                                displayConnector.mEngine.dispatchWallpaperCommand(
+                                        WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras);
+                            } catch (RemoteException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Propagate a sleep event to the wallpaper engine.
+     */
+    public void notifyGoingToSleep(int x, int y, @NonNull Bundle extras) {
+        synchronized (mLock) {
+            final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+            data.connection.forEachDisplayConnector(
+                    displayConnector -> {
+                        if (displayConnector.mEngine != null) {
+                            try {
+                                displayConnector.mEngine.dispatchWallpaperCommand(
+                                        WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1, extras);
+                            } catch (RemoteException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    });
+        }
+    }
+
     @Override
     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index e4dc8c2..f021072 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -96,6 +96,8 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
+import com.android.server.apphibernation.AppHibernationManagerInternal;
+import com.android.server.apphibernation.AppHibernationService;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -163,6 +165,9 @@
      */
     private final LaunchObserverRegistryImpl mLaunchObserver;
     @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
+    private final ArrayMap<String, Boolean> mLastHibernationStates = new ArrayMap<>();
+    private AppHibernationManagerInternal mAppHibernationManagerInternal;
+    private boolean mIsAppHibernationEnabled;
 
     /**
      * The information created when an intent is incoming but we do not yet know whether it will be
@@ -789,6 +794,27 @@
         }
     }
 
+    @Nullable
+    private AppHibernationManagerInternal getAppHibernationManagerInternal() {
+        if (mAppHibernationManagerInternal == null) {
+            mIsAppHibernationEnabled = AppHibernationService.isAppHibernationEnabled();
+            mAppHibernationManagerInternal =
+                    LocalServices.getService(AppHibernationManagerInternal.class);
+        }
+        return mAppHibernationManagerInternal;
+    }
+
+    /**
+     * Notifies the tracker before the package is unstopped because of launching activity.
+     * @param packageName The package to be unstopped.
+     */
+    void notifyBeforePackageUnstopped(@NonNull String packageName) {
+        final AppHibernationManagerInternal ahmInternal = getAppHibernationManagerInternal();
+        if (ahmInternal != null && mIsAppHibernationEnabled) {
+            mLastHibernationStates.put(packageName, ahmInternal.isHibernatingGlobally(packageName));
+        }
+    }
+
     /**
      * Notifies the tracker that we called immediately before we call bindApplication on the client.
      *
@@ -823,6 +849,8 @@
         }
 
         stopLaunchTrace(info);
+        final Boolean isHibernating =
+                mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
         if (abort) {
             mSupervisor.stopWaitingForActivityVisible(info.mLastLaunchedActivity);
             launchObserverNotifyActivityLaunchCancelled(info);
@@ -830,7 +858,7 @@
             if (info.isInterestingToLoggerAndObserver()) {
                 launchObserverNotifyActivityLaunchFinished(info, timestampNs);
             }
-            logAppTransitionFinished(info);
+            logAppTransitionFinished(info, isHibernating != null ? isHibernating : false);
         }
         info.mPendingDrawActivities.clear();
         mTransitionInfoList.remove(info);
@@ -859,7 +887,7 @@
         }
     }
 
-    private void logAppTransitionFinished(@NonNull TransitionInfo info) {
+    private void logAppTransitionFinished(@NonNull TransitionInfo info, boolean isHibernating) {
         if (DEBUG_METRICS) Slog.i(TAG, "logging finished transition " + info);
 
         // Take a snapshot of the transition info before sending it to the handler for logging.
@@ -868,7 +896,7 @@
         if (info.isInterestingToLoggerAndObserver()) {
             BackgroundThread.getHandler().post(() -> logAppTransition(
                     info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs,
-                    infoSnapshot));
+                    infoSnapshot, isHibernating));
         }
         BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
         if (info.mPendingFullyDrawn != null) {
@@ -880,7 +908,7 @@
 
     // This gets called on a background thread without holding the activity manager lock.
     private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
-            TransitionInfoSnapshot info) {
+            TransitionInfoSnapshot info, boolean isHibernating) {
         final LogMaker builder = new LogMaker(APP_TRANSITION);
         builder.setPackageName(info.packageName);
         builder.setType(info.type);
@@ -933,7 +961,8 @@
                 packageOptimizationInfo.getCompilationReason(),
                 packageOptimizationInfo.getCompilationFilter(),
                 info.sourceType,
-                info.sourceEventDelayMs);
+                info.sourceEventDelayMs,
+                isHibernating);
 
         if (DEBUG_METRICS) {
             Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6d72999..aa9727a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -189,6 +189,7 @@
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
@@ -215,14 +216,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.letterboxBackgroundTypeToString;
 import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -268,7 +263,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -344,7 +338,6 @@
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.Task.ActivityState;
 import com.android.server.wm.WindowManagerService.H;
-import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
 import com.android.server.wm.utils.InsetUtils;
 
 import com.google.android.collect.Sets;
@@ -587,10 +580,6 @@
 
     AnimatingActivityRegistry mAnimatingActivityRegistry;
 
-    // Set whenever the ActivityRecord gets reparented to a Task so we can know the last known
-    // parent was when the ActivityRecord is detached from the hierarchy
-    private Task mLastKnownParent;
-
     // Set to the previous Task parent of the ActivityRecord when it is reparented to a new Task
     // due to picture-in-picture. This gets cleared whenever this activity or the Task
     // it references to gets removed. This should also be cleared when we move out of pip.
@@ -649,7 +638,8 @@
      */
     private boolean mWillCloseOrEnterPip;
 
-    private Letterbox mLetterbox;
+    @VisibleForTesting
+    final LetterboxUiController mLetterboxUiController;
 
     /**
      * The scale to fit at least one side of the activity to its parent. If the activity uses
@@ -678,8 +668,6 @@
     @Nullable
     private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
 
-    private boolean mShowWallpaperForLetterboxBackground;
-
     // activity is not displayed?
     // TODO: rename to mNoDisplay
     @VisibleForTesting
@@ -1107,61 +1095,7 @@
             pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
         }
 
-        dumpLetterboxInfo(pw, prefix);
-    }
-
-    private void dumpLetterboxInfo(PrintWriter pw, String prefix) {
-        final WindowState mainWin = findMainWindow();
-        if (mainWin == null) {
-            return;
-        }
-
-        boolean areBoundsLetterboxed = mainWin.isLetterboxedAppWindow();
-        pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
-        if (!areBoundsLetterboxed) {
-            return;
-        }
-
-        pw.println(prefix + "  letterboxReason=" + getLetterboxReasonString(mainWin));
-        pw.println(prefix + "  letterboxAspectRatio=" + computeAspectRatio(getBounds()));
-
-        boolean isLetterboxUiShown = isLetterboxed(mainWin);
-        pw.println(prefix + "isLetterboxUiShown=" + isLetterboxUiShown);
-
-        if (!isLetterboxUiShown) {
-            return;
-        }
-        pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
-                getLetterboxBackgroundColor().toArgb()));
-        pw.println(prefix + "  letterboxBackgroundType="
-                + letterboxBackgroundTypeToString(mWmService.getLetterboxBackgroundType()));
-        if (mWmService.getLetterboxBackgroundType() == LETTERBOX_BACKGROUND_WALLPAPER) {
-            pw.println(prefix + "  isLetterboxWallpaperBlurSupported="
-                    + isLetterboxWallpaperBlurSupported());
-            pw.println(prefix + "  letterboxBackgroundWallpaperDarkScrimAlpha="
-                    + getLetterboxWallpaperDarkScrimAlpha());
-            pw.println(prefix + "  letterboxBackgroundWallpaperBlurRadius="
-                    + getLetterboxWallpaperBlurRadius());
-        }
-        pw.println(prefix + "  letterboxHorizontalPositionMultiplier="
-                + mWmService.getLetterboxHorizontalPositionMultiplier());
-    }
-
-    /**
-     * Returns a string representing the reason for letterboxing. This method assumes the activity
-     * is letterboxed.
-     */
-    private String getLetterboxReasonString(WindowState mainWin) {
-        if (inSizeCompatMode()) {
-            return "SIZE_COMPAT_MODE";
-        }
-        if (isLetterboxedForFixedOrientationAndAspectRatio()) {
-            return "FIXED_ORIENTATION";
-        }
-        if (mainWin.isLetterboxedForDisplayCutout()) {
-            return "DISPLAY_CUTOUT";
-        }
-        return "UNKNOWN_REASON";
+        mLetterboxUiController.dump(pw, prefix);
     }
 
     void setAppTimeTracker(AppTimeTracker att) {
@@ -1361,7 +1295,7 @@
             if (getDisplayContent() != null) {
                 getDisplayContent().mClosingApps.remove(this);
             }
-        } else if (mLastKnownParent != null && mLastKnownParent.getRootTask() != null) {
+        } else if (oldTask != null && oldTask.getRootTask() != null) {
             task.getRootTask().mExitingActivities.remove(this);
         }
         final Task rootTask = getRootTask();
@@ -1374,8 +1308,7 @@
                 ? rootTask.getAnimatingActivityRegistry()
                 : null;
 
-        mLastKnownParent = task;
-        if (mLastKnownParent == mLastParentBeforePip) {
+        if (task == mLastParentBeforePip) {
             // Activity's reparented back from pip, clear the links once established
             clearLastParentBeforePip();
         }
@@ -1451,183 +1384,29 @@
             }
         }
 
-        if (mLetterbox != null) {
-            mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId());
-        }
+        mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());
     }
 
-    // TODO(b/183754168): Move letterbox UI logic into a separate class.
     void layoutLetterbox(WindowState winHint) {
-        final WindowState w = findMainWindow();
-        if (w == null || winHint != null && w != winHint) {
-            return;
-        }
-        final boolean surfaceReady = w.isDrawn()  // Regular case
-                || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
-        final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
-        updateRoundedCorners(w);
-        updateWallpaperForLetterbox(w);
-        if (needsLetterbox) {
-            if (mLetterbox == null) {
-                mLetterbox = new Letterbox(() -> makeChildSurface(null),
-                        mWmService.mTransactionFactory,
-                        mWmService::isLetterboxActivityCornersRounded,
-                        this::getLetterboxBackgroundColor,
-                        this::hasWallpaperBackgroudForLetterbox,
-                        this::getLetterboxWallpaperBlurRadius,
-                        this::getLetterboxWallpaperDarkScrimAlpha);
-                mLetterbox.attachInput(w);
-            }
-            getPosition(mTmpPoint);
-            // Get the bounds of the "space-to-fill". The transformed bounds have the highest
-            // priority because the activity is launched in a rotated environment. In multi-window
-            // mode, the task-level represents this. In fullscreen-mode, the task container does
-            // (since the orientation letterbox is also applied to the task).
-            final Rect transformedBounds = getFixedRotationTransformDisplayBounds();
-            final Rect spaceToFill = transformedBounds != null
-                    ? transformedBounds
-                    : inMultiWindowMode()
-                            ? getRootTask().getBounds()
-                            : getRootTask().getParent().getBounds();
-            mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
-        } else if (mLetterbox != null) {
-            mLetterbox.hide();
-        }
-    }
-
-    private Color getLetterboxBackgroundColor() {
-        final WindowState w = findMainWindow();
-        if (w == null || w.isLetterboxedForDisplayCutout()) {
-            return Color.valueOf(Color.BLACK);
-        }
-        @LetterboxBackgroundType int letterboxBackgroundType =
-                mWmService.getLetterboxBackgroundType();
-        switch (letterboxBackgroundType) {
-            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
-                if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
-                    return Color.valueOf(taskDescription.getBackgroundColorFloating());
-                }
-                break;
-            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
-                if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
-                    return Color.valueOf(taskDescription.getBackgroundColor());
-                }
-                break;
-            case LETTERBOX_BACKGROUND_WALLPAPER:
-                if (hasWallpaperBackgroudForLetterbox()) {
-                    // Color is used for translucent scrim that dims wallpaper.
-                    return Color.valueOf(Color.BLACK);
-                }
-                Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
-                        + "blur is not supported by a device or not supported in the current "
-                        + "window configuration or both alpha scrim and blur radius aren't "
-                        + "provided so using solid color background");
-                break;
-            case LETTERBOX_BACKGROUND_SOLID_COLOR:
-                return mWmService.getLetterboxBackgroundColor();
-            default:
-                throw new AssertionError(
-                    "Unexpected letterbox background type: " + letterboxBackgroundType);
-        }
-        // If picked option configured incorrectly or not supported then default to a solid color
-        // background.
-        return mWmService.getLetterboxBackgroundColor();
-    }
-
-    /**
-     * @return {@code true} when the main window is letterboxed, this activity isn't transparent
-     * and doesn't show a wallpaper.
-     */
-    @VisibleForTesting
-    boolean isLetterboxed(WindowState mainWindow) {
-        return mainWindow.isLetterboxedAppWindow() && fillsParent()
-                // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
-                // WindowContainer#showWallpaper because the later will return true when this
-                // activity is using blurred wallpaper for letterbox backgroud.
-                && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
-    }
-
-    private void updateRoundedCorners(WindowState mainWindow) {
-        int cornersRadius =
-                // Don't round corners if letterboxed only for display cutout.
-                isLetterboxed(mainWindow) && !mainWindow.isLetterboxedForDisplayCutout()
-                        ? Math.max(0, mWmService.getLetterboxActivityCornersRadius()) : 0;
-        setCornersRadius(mainWindow, cornersRadius);
-    }
-
-    private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
-        final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
-        if (windowSurface != null && windowSurface.isValid()) {
-            Transaction transaction = getSyncTransaction();
-            transaction.setCornerRadius(windowSurface, cornersRadius);
-        }
+        mLetterboxUiController.layoutLetterbox(winHint);
     }
 
     boolean hasWallpaperBackgroudForLetterbox() {
-        return mShowWallpaperForLetterboxBackground;
-    }
-
-    private void updateWallpaperForLetterbox(WindowState mainWindow) {
-        @LetterboxBackgroundType int letterboxBackgroundType =
-                mWmService.getLetterboxBackgroundType();
-        boolean wallpaperShouldBeShown =
-                letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
-                        && isLetterboxed(mainWindow)
-                        // Don't use wallpaper as a background if letterboxed for display cutout.
-                        && !mainWindow.isLetterboxedForDisplayCutout()
-                        // Check that dark scrim alpha or blur radius are provided
-                        && (getLetterboxWallpaperBlurRadius() > 0
-                                || getLetterboxWallpaperDarkScrimAlpha() > 0)
-                        // Check that blur is supported by a device if blur radius is provided.
-                        && (getLetterboxWallpaperBlurRadius() <= 0
-                                || isLetterboxWallpaperBlurSupported());
-        if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
-            mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
-            requestUpdateWallpaperIfNeeded();
-        }
-    }
-
-    private int getLetterboxWallpaperBlurRadius() {
-        int blurRadius = mWmService.getLetterboxBackgroundWallpaperBlurRadius();
-        return blurRadius < 0 ? 0 : blurRadius;
-    }
-
-    private float getLetterboxWallpaperDarkScrimAlpha() {
-        float alpha = mWmService.getLetterboxBackgroundWallpaperDarkScrimAlpha();
-        // No scrim by default.
-        return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
-    }
-
-    private boolean isLetterboxWallpaperBlurSupported() {
-        return mWmService.mContext.getSystemService(WindowManager.class).isCrossWindowBlurEnabled();
+        return mLetterboxUiController.hasWallpaperBackgroudForLetterbox();
     }
 
     void updateLetterboxSurface(WindowState winHint) {
-        final WindowState w = findMainWindow();
-        if (w != winHint && winHint != null && w != null) {
-            return;
-        }
-        layoutLetterbox(winHint);
-        if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
-            mLetterbox.applySurfaceChanges(getSyncTransaction());
-        }
+        mLetterboxUiController.updateLetterboxSurface(winHint);
     }
 
+    /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
     Rect getLetterboxInsets() {
-        if (mLetterbox != null) {
-            return mLetterbox.getInsets();
-        } else {
-            return new Rect();
-        }
+        return mLetterboxUiController.getLetterboxInsets();
     }
 
     /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
     void getLetterboxInnerBounds(Rect outBounds) {
-        if (mLetterbox != null) {
-            outBounds.set(mLetterbox.getInnerFrame());
-        } else {
-            outBounds.setEmpty();
-        }
+        mLetterboxUiController.getLetterboxInnerBounds(outBounds);
     }
 
     /**
@@ -1635,7 +1414,7 @@
      *     when the current activity is displayed.
      */
     boolean isFullyTransparentBarAllowed(Rect rect) {
-        return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+        return mLetterboxUiController.isFullyTransparentBarAllowed(rect);
     }
 
     /**
@@ -1643,7 +1422,7 @@
      * the given {@code rect}.
      */
     boolean isLetterboxOverlappingWith(Rect rect) {
-        return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+        return mLetterboxUiController.isLetterboxOverlappingWith(rect);
     }
 
     static class Token extends IApplicationToken.Stub {
@@ -1892,6 +1671,9 @@
 
         mPersistentState = persistentState;
         taskDescription = _taskDescription;
+
+        mLetterboxUiController = new LetterboxUiController(mWmService, this);
+
         if (_createTime > 0) {
             createTime = _createTime;
         }
@@ -2176,7 +1958,6 @@
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Add starting %s: startingData=%s",
                     this, startingData);
 
-
             WindowManagerPolicy.StartingSurface surface = null;
             try {
                 surface = startingData.createStartingSurface(ActivityRecord.this);
@@ -3724,10 +3505,8 @@
             dc.setFocusedApp(null);
             mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
         }
-        if (mLetterbox != null) {
-            mLetterbox.destroy();
-            mLetterbox = null;
-        }
+
+        mLetterboxUiController.destroy();
 
         if (!delayed) {
             updateReportedVisibilityLocked();
@@ -7155,7 +6934,8 @@
             offsetX = getHorizontalCenterOffset(
                     parentAppBounds.width(), screenResolvedBounds.width());
         } else {
-            float positionMultiplier = mWmService.getLetterboxHorizontalPositionMultiplier();
+            float positionMultiplier =
+                    mWmService.mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier();
             positionMultiplier =
                     (positionMultiplier < 0.0f || positionMultiplier > 1.0f)
                             // Default to central position if invalid value is provided.
@@ -7228,7 +7008,7 @@
         // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
         // set-fixed-orientation-letterbox-aspect-ratio.
         final float letterboxAspectRatioOverride =
-                mWmService.getFixedOrientationLetterboxAspectRatio();
+                mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
         aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
                 ? letterboxAspectRatioOverride : aspect;
 
@@ -7746,7 +7526,7 @@
     /**
      * Returns the aspect ratio of the given {@code rect}.
      */
-    private static float computeAspectRatio(Rect rect) {
+    static float computeAspectRatio(Rect rect) {
         final int width = rect.width();
         final int height = rect.height();
         if (width == 0 || height == 0) {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
new file mode 100644
index 0000000..eb7087c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.graphics.Color;
+
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Reads letterbox configs from resources and controls their overrides at runtime. */
+final class LetterboxConfiguration {
+
+    /**
+     * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
+     * set-fixed-orientation-letterbox-aspect-ratio or via {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
+     * if it is <= this value.
+     */
+    static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
+
+    /** Enum for Letterbox background type. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
+            LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER})
+    @interface LetterboxBackgroundType {};
+    /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
+    static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
+
+    /** Color specified in R.attr.colorBackground for the letterboxed application. */
+    static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
+
+    /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
+    static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
+
+    /** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
+    static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
+
+    final Context mContext;
+
+    // Aspect ratio of letterbox for fixed orientation, values <=
+    // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
+    private float mFixedOrientationLetterboxAspectRatio;
+
+    // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
+    private int mLetterboxActivityCornersRadius;
+
+    // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+    private Color mLetterboxBackgroundColor;
+
+    @LetterboxBackgroundType
+    private int mLetterboxBackgroundType;
+
+    // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType.
+    // Values <= 0 are ignored and 0 is used instead.
+    private int mLetterboxBackgroundWallpaperBlurRadius;
+
+    // Alpha of a black scrim shown over wallpaper letterbox background when
+    // LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType.
+    // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead.
+    private float mLetterboxBackgroundWallpaperDarkScrimAlpha;
+
+    // Horizontal position of a center of the letterboxed app window. 0 corresponds to the left
+    // side of the screen and 1.0 to the right side.
+    private float mLetterboxHorizontalPositionMultiplier;
+
+    LetterboxConfiguration(Context context) {
+        mContext = context;
+        mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
+                R.dimen.config_fixedOrientationLetterboxAspectRatio);
+        mLetterboxActivityCornersRadius = context.getResources().getInteger(
+                R.integer.config_letterboxActivityCornersRadius);
+        mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
+                R.color.config_letterboxBackgroundColor));
+        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
+        mLetterboxBackgroundWallpaperBlurRadius = context.getResources().getDimensionPixelSize(
+                R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+        mLetterboxBackgroundWallpaperDarkScrimAlpha = context.getResources().getFloat(
+                R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+        mLetterboxHorizontalPositionMultiplier = context.getResources().getFloat(
+                R.dimen.config_letterboxHorizontalPositionMultiplier);
+    }
+
+    /**
+     * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
+     * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
+     * the framework implementation will be used to determine the aspect ratio.
+     */
+    void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
+        mFixedOrientationLetterboxAspectRatio = aspectRatio;
+    }
+
+    /**
+     * Resets the aspect ratio of letterbox for fixed orientation to {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
+     */
+    void resetFixedOrientationLetterboxAspectRatio() {
+        mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
+    }
+
+    /**
+     * Gets the aspect ratio of letterbox for fixed orientation.
+     */
+    float getFixedOrientationLetterboxAspectRatio() {
+        return mFixedOrientationLetterboxAspectRatio;
+    }
+
+    /**
+     * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+     * both it and a value of {@link
+     * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+     * and corners of the activity won't be rounded.
+     */
+    void setLetterboxActivityCornersRadius(int cornersRadius) {
+        mLetterboxActivityCornersRadius = cornersRadius;
+    }
+
+    /**
+     * Resets corners raidus for activities presented in the letterbox mode to {@link
+     * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+     */
+    void resetLetterboxActivityCornersRadius() {
+        mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+    }
+
+    /**
+     * Whether corners of letterboxed activities are rounded.
+     */
+    boolean isLetterboxActivityCornersRounded() {
+        return getLetterboxActivityCornersRadius() > 0;
+    }
+
+    /**
+     * Gets corners raidus for activities presented in the letterbox mode.
+     */
+    int getLetterboxActivityCornersRadius() {
+        return mLetterboxActivityCornersRadius;
+    }
+
+    /**
+     * Gets color of letterbox background which is  used when {@link
+     * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+     * fallback for other backfround types.
+     */
+    Color getLetterboxBackgroundColor() {
+        return mLetterboxBackgroundColor;
+    }
+
+
+    /**
+     * Sets color of letterbox background which is used when {@link
+     * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+     * fallback for other backfround types.
+     */
+    void setLetterboxBackgroundColor(Color color) {
+        mLetterboxBackgroundColor = color;
+    }
+
+    /**
+     * Resets color of letterbox background to {@link
+     * com.android.internal.R.color.config_letterboxBackgroundColor}.
+     */
+    void resetLetterboxBackgroundColor() {
+        mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+                com.android.internal.R.color.config_letterboxBackgroundColor));
+    }
+
+    /**
+     * Gets {@link LetterboxBackgroundType} specified in {@link
+     * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+     */
+    @LetterboxBackgroundType
+    int getLetterboxBackgroundType() {
+        return mLetterboxBackgroundType;
+    }
+
+    /** Sets letterbox background type. */
+    void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+        mLetterboxBackgroundType = backgroundType;
+    }
+
+    /**
+     * Resets cletterbox background type to {@link
+     * com.android.internal.R.integer.config_letterboxBackgroundType}.
+     */
+    void resetLetterboxBackgroundType() {
+        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+    }
+
+    /** Returns a string representing the given {@link LetterboxBackgroundType}. */
+    static String letterboxBackgroundTypeToString(
+            @LetterboxBackgroundType int backgroundType) {
+        switch (backgroundType) {
+            case LETTERBOX_BACKGROUND_SOLID_COLOR:
+                return "LETTERBOX_BACKGROUND_SOLID_COLOR";
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+                return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+                return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
+            case LETTERBOX_BACKGROUND_WALLPAPER:
+                return "LETTERBOX_BACKGROUND_WALLPAPER";
+            default:
+                return "unknown=" + backgroundType;
+        }
+    }
+
+    @LetterboxBackgroundType
+    private static int readLetterboxBackgroundTypeFromConfig(Context context) {
+        int backgroundType = context.getResources().getInteger(
+                com.android.internal.R.integer.config_letterboxBackgroundType);
+        return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
+                    || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
+                    || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
+                    || backgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+                    ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
+    }
+
+    /**
+     * Overrides alpha of a black scrim shown over wallpaper for {@link
+     * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
+     *
+     * <p>If given value is < 0 or >= 1, both it and a value of {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
+     * and 0.0 (transparent) is instead.
+     */
+    void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
+        mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
+    }
+
+    /**
+     * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
+     */
+    void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
+        mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+    }
+
+    /**
+     * Gets alpha of a black scrim shown over wallpaper letterbox background.
+     */
+    float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
+        return mLetterboxBackgroundWallpaperDarkScrimAlpha;
+    }
+
+    /**
+     * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
+     * {@link mLetterboxBackgroundType}.
+     *
+     * <p> If given value <= 0, both it and a value of {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
+     * and 0 is used instead.
+     */
+    void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
+        mLetterboxBackgroundWallpaperBlurRadius = radius;
+    }
+
+    /**
+     * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+     * mLetterboxBackgroundType} to {@link
+     * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
+     */
+    void resetLetterboxBackgroundWallpaperBlurRadius() {
+        mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+    }
+
+    /**
+     * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+     * mLetterboxBackgroundType}.
+     */
+    int getLetterboxBackgroundWallpaperBlurRadius() {
+        return mLetterboxBackgroundWallpaperBlurRadius;
+    }
+
+    /*
+     * Gets horizontal position of a center of the letterboxed app window specified
+     * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
+     * or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
+     * right side.
+     *
+     * <p>This value can be outside of [0, 1] range so clients need to check and default to the
+     * central position (0.5).
+     */
+    float getLetterboxHorizontalPositionMultiplier() {
+        return mLetterboxHorizontalPositionMultiplier;
+    }
+
+    /**
+     * Overrides horizontal position of a center of the letterboxed app window. If given value < 0
+     * or > 1, then it and a value of {@link
+     * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
+     * central position (0.5) is used.
+     */
+    void setLetterboxHorizontalPositionMultiplier(float multiplier) {
+        mLetterboxHorizontalPositionMultiplier = multiplier;
+    }
+
+    /**
+     * Resets horizontal position of a center of the letterboxed app window to {@link
+     * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+     */
+    void resetLetterboxHorizontalPositionMultiplier() {
+        mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
new file mode 100644
index 0000000..130f680
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTypeToString;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.TaskDescription;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
+
+import java.io.PrintWriter;
+
+/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
+// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
+// SizeCompatTests and LetterboxTests but not all.
+// TODO(b/185264020): Consider making LetterboxUiController applicable to any level of the
+// hierarchy in addition to ActivityRecord (Task, DisplayArea, ...).
+final class LetterboxUiController {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
+
+    private final Point mTmpPoint = new Point();
+
+    private final LetterboxConfiguration mLetterboxConfiguration;
+    private final ActivityRecord mActivityRecord;
+
+    private boolean mShowWallpaperForLetterboxBackground;
+
+    @Nullable
+    private Letterbox mLetterbox;
+
+    LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
+        mLetterboxConfiguration = wmService.mLetterboxConfiguration;
+        // Given activityRecord may not be fully constructed since LetterboxUiController
+        // is created in its constructor. It shouldn't be used in this constructor but it's safe
+        // to use it after since controller is only used in ActivityRecord.
+        mActivityRecord = activityRecord;
+    }
+
+    /** Cleans up {@link Letterbox} if it exists.*/
+    void destroy() {
+        if (mLetterbox != null) {
+            mLetterbox.destroy();
+            mLetterbox = null;
+        }
+    }
+
+    void onMovedToDisplay(int displayId) {
+        if (mLetterbox != null) {
+            mLetterbox.onMovedToDisplay(displayId);
+        }
+    }
+
+    boolean hasWallpaperBackgroudForLetterbox() {
+        return mShowWallpaperForLetterboxBackground;
+    }
+
+    /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+    Rect getLetterboxInsets() {
+        if (mLetterbox != null) {
+            return mLetterbox.getInsets();
+        } else {
+            return new Rect();
+        }
+    }
+
+    /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
+    void getLetterboxInnerBounds(Rect outBounds) {
+        if (mLetterbox != null) {
+            outBounds.set(mLetterbox.getInnerFrame());
+        } else {
+            outBounds.setEmpty();
+        }
+    }
+
+    /**
+     * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
+     *     when the current activity is displayed.
+     */
+    boolean isFullyTransparentBarAllowed(Rect rect) {
+        return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+    }
+
+    /**
+     * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with
+     * the given {@code rect}.
+     */
+    boolean isLetterboxOverlappingWith(Rect rect) {
+        return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+    }
+
+    void updateLetterboxSurface(WindowState winHint) {
+        final WindowState w = mActivityRecord.findMainWindow();
+        if (w != winHint && winHint != null && w != null) {
+            return;
+        }
+        layoutLetterbox(winHint);
+        if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
+            mLetterbox.applySurfaceChanges(mActivityRecord.getSyncTransaction());
+        }
+    }
+
+    void layoutLetterbox(WindowState winHint) {
+        final WindowState w = mActivityRecord.findMainWindow();
+        if (w == null || winHint != null && w != winHint) {
+            return;
+        }
+        final boolean surfaceReady = w.isDrawn()  // Regular case
+                || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
+        final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
+        updateRoundedCorners(w);
+        updateWallpaperForLetterbox(w);
+        if (needsLetterbox) {
+            if (mLetterbox == null) {
+                mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
+                        mActivityRecord.mWmService.mTransactionFactory,
+                        mLetterboxConfiguration::isLetterboxActivityCornersRounded,
+                        this::getLetterboxBackgroundColor,
+                        this::hasWallpaperBackgroudForLetterbox,
+                        this::getLetterboxWallpaperBlurRadius,
+                        this::getLetterboxWallpaperDarkScrimAlpha);
+                mLetterbox.attachInput(w);
+            }
+            mActivityRecord.getPosition(mTmpPoint);
+            // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+            // priority because the activity is launched in a rotated environment. In multi-window
+            // mode, the task-level represents this. In fullscreen-mode, the task container does
+            // (since the orientation letterbox is also applied to the task).
+            final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
+            final Rect spaceToFill = transformedBounds != null
+                    ? transformedBounds
+                    : mActivityRecord.inMultiWindowMode()
+                            ? mActivityRecord.getRootTask().getBounds()
+                            : mActivityRecord.getRootTask().getParent().getBounds();
+            mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
+        } else if (mLetterbox != null) {
+            mLetterbox.hide();
+        }
+    }
+
+    /**
+     * @return {@code true} when the main window is letterboxed, this activity isn't transparent
+     * and doesn't show a wallpaper.
+     */
+    @VisibleForTesting
+    boolean isLetterboxed(WindowState mainWindow) {
+        return mainWindow.isLetterboxedAppWindow() && mActivityRecord.fillsParent()
+                // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
+                // WindowContainer#showWallpaper because the later will return true when this
+                // activity is using blurred wallpaper for letterbox backgroud.
+                && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
+    }
+
+    private Color getLetterboxBackgroundColor() {
+        final WindowState w = mActivityRecord.findMainWindow();
+        if (w == null || w.isLetterboxedForDisplayCutout()) {
+            return Color.valueOf(Color.BLACK);
+        }
+        @LetterboxBackgroundType int letterboxBackgroundType =
+                mLetterboxConfiguration.getLetterboxBackgroundType();
+        TaskDescription taskDescription = mActivityRecord.taskDescription;
+        switch (letterboxBackgroundType) {
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+                if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+                    return Color.valueOf(taskDescription.getBackgroundColorFloating());
+                }
+                break;
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+                if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+                    return Color.valueOf(taskDescription.getBackgroundColor());
+                }
+                break;
+            case LETTERBOX_BACKGROUND_WALLPAPER:
+                if (hasWallpaperBackgroudForLetterbox()) {
+                    // Color is used for translucent scrim that dims wallpaper.
+                    return Color.valueOf(Color.BLACK);
+                }
+                Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
+                        + "blur is not supported by a device or not supported in the current "
+                        + "window configuration or both alpha scrim and blur radius aren't "
+                        + "provided so using solid color background");
+                break;
+            case LETTERBOX_BACKGROUND_SOLID_COLOR:
+                return mLetterboxConfiguration.getLetterboxBackgroundColor();
+            default:
+                throw new AssertionError(
+                    "Unexpected letterbox background type: " + letterboxBackgroundType);
+        }
+        // If picked option configured incorrectly or not supported then default to a solid color
+        // background.
+        return mLetterboxConfiguration.getLetterboxBackgroundColor();
+    }
+
+    private void updateRoundedCorners(WindowState mainWindow) {
+        int cornersRadius =
+                // Don't round corners if letterboxed only for display cutout.
+                isLetterboxed(mainWindow)
+                                && !mainWindow.isLetterboxedForDisplayCutout()
+                        ? Math.max(0, mLetterboxConfiguration.getLetterboxActivityCornersRadius())
+                        : 0;
+        setCornersRadius(mainWindow, cornersRadius);
+    }
+
+    private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
+        final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
+        if (windowSurface != null && windowSurface.isValid()) {
+            Transaction transaction = mActivityRecord.getSyncTransaction();
+            transaction.setCornerRadius(windowSurface, cornersRadius);
+        }
+    }
+
+    private void updateWallpaperForLetterbox(WindowState mainWindow) {
+        @LetterboxBackgroundType int letterboxBackgroundType =
+                mLetterboxConfiguration.getLetterboxBackgroundType();
+        boolean wallpaperShouldBeShown =
+                letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+                        && isLetterboxed(mainWindow)
+                        // Don't use wallpaper as a background if letterboxed for display cutout.
+                        && !mainWindow.isLetterboxedForDisplayCutout()
+                        // Check that dark scrim alpha or blur radius are provided
+                        && (getLetterboxWallpaperBlurRadius() > 0
+                                || getLetterboxWallpaperDarkScrimAlpha() > 0)
+                        // Check that blur is supported by a device if blur radius is provided.
+                        && (getLetterboxWallpaperBlurRadius() <= 0
+                                || isLetterboxWallpaperBlurSupported());
+        if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
+            mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
+            mActivityRecord.requestUpdateWallpaperIfNeeded();
+        }
+    }
+
+    private int getLetterboxWallpaperBlurRadius() {
+        int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius();
+        return blurRadius < 0 ? 0 : blurRadius;
+    }
+
+    private float getLetterboxWallpaperDarkScrimAlpha() {
+        float alpha = mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
+        // No scrim by default.
+        return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
+    }
+
+    private boolean isLetterboxWallpaperBlurSupported() {
+        return mLetterboxConfiguration.mContext.getSystemService(WindowManager.class)
+                .isCrossWindowBlurEnabled();
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        final WindowState mainWin = mActivityRecord.findMainWindow();
+        if (mainWin == null) {
+            return;
+        }
+
+        boolean areBoundsLetterboxed = mainWin.isLetterboxedAppWindow();
+        pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
+        if (!areBoundsLetterboxed) {
+            return;
+        }
+
+        pw.println(prefix + "  letterboxReason=" + getLetterboxReasonString(mainWin));
+        pw.println(prefix + "  letterboxAspectRatio="
+                + mActivityRecord.computeAspectRatio(mActivityRecord.getBounds()));
+
+        boolean isLetterboxUiShown = isLetterboxed(mainWin);
+        pw.println(prefix + "isLetterboxUiShown=" + isLetterboxUiShown);
+
+        if (!isLetterboxUiShown) {
+            return;
+        }
+        pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
+                getLetterboxBackgroundColor().toArgb()));
+        pw.println(prefix + "  letterboxBackgroundType="
+                + letterboxBackgroundTypeToString(
+                        mLetterboxConfiguration.getLetterboxBackgroundType()));
+        if (mLetterboxConfiguration.getLetterboxBackgroundType()
+                == LETTERBOX_BACKGROUND_WALLPAPER) {
+            pw.println(prefix + "  isLetterboxWallpaperBlurSupported="
+                    + isLetterboxWallpaperBlurSupported());
+            pw.println(prefix + "  letterboxBackgroundWallpaperDarkScrimAlpha="
+                    + getLetterboxWallpaperDarkScrimAlpha());
+            pw.println(prefix + "  letterboxBackgroundWallpaperBlurRadius="
+                    + getLetterboxWallpaperBlurRadius());
+        }
+        pw.println(prefix + "  letterboxHorizontalPositionMultiplier="
+                + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+    }
+
+    /**
+     * Returns a string representing the reason for letterboxing. This method assumes the activity
+     * is letterboxed.
+     */
+    private String getLetterboxReasonString(WindowState mainWin) {
+        if (mActivityRecord.inSizeCompatMode()) {
+            return "SIZE_COMPAT_MODE";
+        }
+        if (mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio()) {
+            return "FIXED_ORIENTATION";
+        }
+        if (mainWin.isLetterboxedForDisplayCutout()) {
+            return "DISPLAY_CUTOUT";
+        }
+        return "UNKNOWN_REASON";
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 95a4f69e..fb66c04 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -36,9 +36,11 @@
 import android.animation.ArgbEvaluator;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -210,9 +212,9 @@
             String name = "RotationLayer";
             mScreenshotLayer = displayContent.makeOverlay()
                     .setName(name)
-                    .setBufferSize(mWidth, mHeight)
                     .setSecure(isSecure)
                     .setCallsite("ScreenRotationAnimation")
+                    .setBLASTLayer()
                     .build();
             // This is the way to tell the input system to exclude this surface from occlusion
             // detection since we don't have a window for it. We do this because this window is
@@ -225,32 +227,29 @@
                     .setCallsite("ScreenRotationAnimation")
                     .build();
 
-            final Surface surface = mService.mSurfaceFactory.get();
-            // In case display bounds change, screenshot buffer and surface may mismatch so
-            // set a scaling mode.
-            surface.copyFrom(mScreenshotLayer);
-            surface.setScalingMode(Surface.SCALING_MODE_SCALE_TO_WINDOW);
-
+            HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                     "ScreenRotationAnimation#getMedianBorderLuma");
-            mStartLuma = RotationAnimationUtils.getMedianBorderLuma(
-                    screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace());
+            mStartLuma = RotationAnimationUtils.getMedianBorderLuma(hardwareBuffer,
+                    screenshotBuffer.getColorSpace());
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-            try {
-                surface.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
-                        screenshotBuffer.getColorSpace());
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
-            }
+
+            GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
+                    screenshotBuffer.getHardwareBuffer());
+            // Scale the layer to the display size.
+            float dsdx = (float) mWidth / hardwareBuffer.getWidth();
+            float dsdy = (float) mHeight / hardwareBuffer.getHeight();
 
             t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
             t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
             t.setLayer(mBackColorSurface, -1);
             t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
             t.setAlpha(mBackColorSurface, 1);
+            t.setBuffer(mScreenshotLayer, buffer);
+            t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
+            t.setMatrix(mScreenshotLayer, dsdx, 0, 0, dsdy);
             t.show(mScreenshotLayer);
             t.show(mBackColorSurface);
-            surface.destroy();
 
         } catch (OutOfResourcesException e) {
             Slog.w(TAG, "Unable to allocate freeze surface", e);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b82a308..c6cd560 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -640,9 +640,16 @@
         }
     }
 
-    void windowAddedLocked(String packageName) {
-        mPackageName = packageName;
-        mRelayoutTag = "relayoutWindow: " + mPackageName;
+    void windowAddedLocked() {
+        if (mPackageName == null) {
+            final WindowProcessController wpc = mService.mAtmService.mProcessMap.getProcess(mPid);
+            if (wpc != null) {
+                mPackageName = wpc.mInfo.packageName;
+                mRelayoutTag = "relayoutWindow: " + mPackageName;
+            } else {
+                Slog.e(TAG_WM, "Unknown process pid=" + mPid);
+            }
+        }
         if (mSurfaceSession == null) {
             if (DEBUG) {
                 Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 140ae3e..603bfd1 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -58,10 +58,12 @@
                     overrideConfig, displayId);
         }
 
-        final Task task = activity.getTask();
-        if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
-                activity.token, theme)) {
-            return new ShellStartingSurface(task);
+        synchronized (mService.mGlobalLock) {
+            final Task task = activity.getTask();
+            if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
+                    task, activity.token, theme)) {
+                return new ShellStartingSurface(task);
+            }
         }
         return null;
     }
@@ -124,14 +126,13 @@
                 activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
                         topFullscreenActivity, false /* checkOpening */);
             }
+            if (DEBUG_ENABLE_SHELL_DRAWER) {
+                mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
+                        activity.token, 0 /* launchTheme */);
+                return new ShellStartingSurface(task);
+            }
         }
-        if (!DEBUG_ENABLE_SHELL_DRAWER) {
-            return mService.mTaskSnapshotController
-                    .createStartingSurface(activity, taskSnapshot);
-        }
-        mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, activity.token,
-                0 /* launchTheme */);
-        return new ShellStartingSurface(task);
+        return mService.mTaskSnapshotController.createStartingSurface(activity, taskSnapshot);
     }
 
 
@@ -144,8 +145,9 @@
 
         @Override
         public void remove(boolean animate) {
-            mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
-                    animate);
+            synchronized (mService.mGlobalLock) {
+                mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, animate);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 6f434e0..e0a791e 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
@@ -153,29 +154,24 @@
          */
         Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
                 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
-            Surface drawSurface = surfaceFactory.get();
             // We can't use a delegating constructor since we need to
             // reference this::onAnimationFinished
-            HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
-            final int width = hardwareBuffer.getWidth();
-            final int height = hardwareBuffer.getHeight();
+            GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+                    screenshotBuffer.getHardwareBuffer());
 
             mSurfaceControl = mAnimatable.makeAnimationLeash()
                     .setName("snapshot anim: " + mAnimatable.toString())
-                    .setBufferSize(width, height)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .setParent(parent)
                     .setSecure(screenshotBuffer.containsSecureLayers())
                     .setCallsite("SurfaceFreezer.Snapshot")
+                    .setBLASTLayer()
                     .build();
 
             ProtoLog.i(WM_SHOW_TRANSACTIONS, "  THUMBNAIL %s: CREATE", mSurfaceControl);
 
-            // Transfer the thumbnail to the surface
-            drawSurface.copyFrom(mSurfaceControl);
-            drawSurface.attachAndQueueBufferWithColorSpace(hardwareBuffer,
-                    screenshotBuffer.getColorSpace());
-            drawSurface.release();
+            t.setBuffer(mSurfaceControl, graphicBuffer);
+            t.setColorSpace(mSurfaceControl, screenshotBuffer.getColorSpace());
             t.show(mSurfaceControl);
 
             // We parent the thumbnail to the container, and just place it on top of anything else
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 07f81c7..858d9f3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6309,6 +6309,8 @@
         // Launching this app's activity, make sure the app is no longer
         // considered stopped.
         try {
+            mTaskSupervisor.getActivityMetricsLogger()
+                    .notifyBeforePackageUnstopped(next.packageName);
             mAtmService.getPackageManager().setPackageStoppedState(
                     next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
         } catch (RemoteException e1) {
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index dff621c..625cff3 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -439,6 +439,13 @@
             taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
         }
 
+        // Re-route to default display if the home activity doesn't support multi-display
+        if (taskDisplayArea != null && activityRecord.isActivityTypeHome()
+                && !mSupervisor.mRootWindowContainer.canStartHomeOnDisplayArea(activityRecord.info,
+                        taskDisplayArea, false /* allowInstrumenting */)) {
+            taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+        }
+
         return (taskDisplayArea != null)
                 ? taskDisplayArea
                 : getFallbackDisplayAreaForActivity(activityRecord, request);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0c9473a..d14a773 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -159,7 +159,6 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -977,56 +976,7 @@
     private boolean mAnimationsDisabled = false;
     boolean mPointerLocationEnabled = false;
 
-    /**
-     * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
-     * set-fixed-orientation-letterbox-aspect-ratio or via {@link
-     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
-     * if it is <= this value.
-     */
-    static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
-
-    /** Enum for Letterbox background type. */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
-            LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER})
-    @interface LetterboxBackgroundType {};
-    /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
-    static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
-
-    /** Color specified in R.attr.colorBackground for the letterboxed application. */
-    static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
-
-    /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
-    static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
-
-    /** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
-    static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
-
-    // Aspect ratio of letterbox for fixed orientation, values <=
-    // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
-    private float mFixedOrientationLetterboxAspectRatio;
-
-    // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
-    private int mLetterboxActivityCornersRadius;
-
-    // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
-    private Color mLetterboxBackgroundColor;
-
-    @LetterboxBackgroundType
-    private int mLetterboxBackgroundType;
-
-    // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType.
-    // Values <= 0 are ignored and 0 is used instead.
-    private int mLetterboxBackgroundWallpaperBlurRadius;
-
-    // Alpha of a black scrim shown over wallpaper letterbox background when
-    // LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType.
-    // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead.
-    private float mLetterboxBackgroundWallpaperDarkScrimAlpha;
-
-    // horizontal position of a center of the letterboxed app window. 0 corresponds to the left
-    // side of the screen and 1.0 to the right side.
-    private float mLetterboxHorizontalPositionMultiplier;
+    final LetterboxConfiguration mLetterboxConfiguration;
 
     final InputManagerService mInputManager;
     final DisplayManagerInternal mDisplayManagerInternal;
@@ -1258,19 +1208,7 @@
         mAssistantOnTopOfDream = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_assistantOnTopOfDream);
 
-        mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
-                com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
-        mLetterboxActivityCornersRadius = context.getResources().getInteger(
-                com.android.internal.R.integer.config_letterboxActivityCornersRadius);
-        mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
-                com.android.internal.R.color.config_letterboxBackgroundColor));
-        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
-        mLetterboxBackgroundWallpaperBlurRadius = context.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
-        mLetterboxBackgroundWallpaperDarkScrimAlpha = context.getResources().getFloat(
-                com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
-        mLetterboxHorizontalPositionMultiplier = context.getResources().getFloat(
-                com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
+        mLetterboxConfiguration = new LetterboxConfiguration(context);
 
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -3869,233 +3807,6 @@
         }
     }
 
-    /**
-     * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
-     * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
-     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
-     * the framework implementation will be used to determine the aspect ratio.
-     */
-    void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
-        mFixedOrientationLetterboxAspectRatio = aspectRatio;
-    }
-
-    /**
-     * Resets the aspect ratio of letterbox for fixed orientation to {@link
-     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
-     */
-    void resetFixedOrientationLetterboxAspectRatio() {
-        mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
-                com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
-    }
-
-    /**
-     * Gets the aspect ratio of letterbox for fixed orientation.
-     */
-    float getFixedOrientationLetterboxAspectRatio() {
-        return mFixedOrientationLetterboxAspectRatio;
-    }
-
-    /**
-     * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
-     * both it and a value of {@link
-     * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
-     * and corners of the activity won't be rounded.
-     */
-    void setLetterboxActivityCornersRadius(int cornersRadius) {
-        mLetterboxActivityCornersRadius = cornersRadius;
-    }
-
-    /**
-     * Resets corners raidus for activities presented in the letterbox mode to {@link
-     * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
-     */
-    void resetLetterboxActivityCornersRadius() {
-        mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_letterboxActivityCornersRadius);
-    }
-
-    /**
-     * Whether corners of letterboxed activities are rounded.
-     */
-    boolean isLetterboxActivityCornersRounded() {
-        return getLetterboxActivityCornersRadius() > 0;
-    }
-
-    /**
-     * Gets corners raidus for activities presented in the letterbox mode.
-     */
-    int getLetterboxActivityCornersRadius() {
-        return mLetterboxActivityCornersRadius;
-    }
-
-    /**
-     * Gets color of letterbox background which is  used when {@link
-     * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
-     * fallback for other backfround types.
-     */
-    Color getLetterboxBackgroundColor() {
-        return mLetterboxBackgroundColor;
-    }
-
-
-    /**
-     * Sets color of letterbox background which is used when {@link
-     * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
-     * fallback for other backfround types.
-     */
-    void setLetterboxBackgroundColor(Color color) {
-        mLetterboxBackgroundColor = color;
-    }
-
-    /**
-     * Resets color of letterbox background to {@link
-     * com.android.internal.R.color.config_letterboxBackgroundColor}.
-     */
-    void resetLetterboxBackgroundColor() {
-        mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
-                com.android.internal.R.color.config_letterboxBackgroundColor));
-    }
-
-    /**
-     * Gets {@link LetterboxBackgroundType} specified in {@link
-     * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
-     */
-    @LetterboxBackgroundType
-    int getLetterboxBackgroundType() {
-        return mLetterboxBackgroundType;
-    }
-
-    /** Sets letterbox background type. */
-    void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
-        mLetterboxBackgroundType = backgroundType;
-    }
-
-    /**
-     * Resets cletterbox background type to {@link
-     * com.android.internal.R.integer.config_letterboxBackgroundType}.
-     */
-    void resetLetterboxBackgroundType() {
-        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
-    }
-
-    @LetterboxBackgroundType
-    private static int readLetterboxBackgroundTypeFromConfig(Context context) {
-        int backgroundType = context.getResources().getInteger(
-                com.android.internal.R.integer.config_letterboxBackgroundType);
-        return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
-                    || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
-                    || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
-                    || backgroundType == LETTERBOX_BACKGROUND_WALLPAPER
-                    ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
-    }
-
-    /** Returns a string representing the given {@link LetterboxBackgroundType}. */
-    static String letterboxBackgroundTypeToString(
-            @LetterboxBackgroundType int backgroundType) {
-        switch (backgroundType) {
-            case LETTERBOX_BACKGROUND_SOLID_COLOR:
-                return "LETTERBOX_BACKGROUND_SOLID_COLOR";
-            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
-                return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
-            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
-                return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
-            case LETTERBOX_BACKGROUND_WALLPAPER:
-                return "LETTERBOX_BACKGROUND_WALLPAPER";
-            default:
-                return "unknown=" + backgroundType;
-        }
-    }
-
-    /**
-     * Overrides alpha of a black scrim shown over wallpaper for {@link
-     * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
-     *
-     * <p>If given value is < 0 or >= 1, both it and a value of {@link
-     * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
-     * and 0.0 (transparent) is instead.
-     */
-    void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
-        mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
-    }
-
-    /**
-     * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
-     * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
-     */
-    void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
-        mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
-                com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
-    }
-
-    /**
-     * Gets alpha of a black scrim shown over wallpaper letterbox background.
-     */
-    float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
-        return mLetterboxBackgroundWallpaperDarkScrimAlpha;
-    }
-
-    /**
-     * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
-     * {@link mLetterboxBackgroundType}.
-     *
-     * <p> If given value <= 0, both it and a value of {@link
-     * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
-     * and 0 is used instead.
-     */
-    void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
-        mLetterboxBackgroundWallpaperBlurRadius = radius;
-    }
-
-    /**
-     * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
-     * mLetterboxBackgroundType} to {@link
-     * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
-     */
-    void resetLetterboxBackgroundWallpaperBlurRadius() {
-        mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
-    }
-
-    /**
-     * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
-     * mLetterboxBackgroundType}.
-     */
-    int getLetterboxBackgroundWallpaperBlurRadius() {
-        return mLetterboxBackgroundWallpaperBlurRadius;
-    }
-
-    /*
-     * Gets horizontal position of a center of the letterboxed app window specified
-     * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
-     * or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
-     * right side.
-     *
-     * <p>This value can be outside of [0, 1] range so clients need to check and default to the
-     * central position (0.5).
-     */
-    float getLetterboxHorizontalPositionMultiplier() {
-        return mLetterboxHorizontalPositionMultiplier;
-    }
-
-    /**
-     * Overrides horizontal position of a center of the letterboxed app window. If given value < 0
-     * or > 1, then it and a value of {@link
-     * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
-     * central position (0.5) is used.
-     */
-    void setLetterboxHorizontalPositionMultiplier(float multiplier) {
-        mLetterboxHorizontalPositionMultiplier = multiplier;
-    }
-
-    /**
-     * Resets horizontal position of a center of the letterboxed app window to {@link
-     * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
-     */
-    void resetLetterboxHorizontalPositionMultiplier() {
-        mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
-                com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
-    }
-
     @Override
     public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
         if (!checkCallingPermission(
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 68257d4..1b578d1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,10 +19,10 @@
 import static android.os.Build.IS_USER;
 import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
 
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
 
 import android.graphics.Color;
 import android.graphics.Point;
@@ -41,7 +41,7 @@
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.server.LocalServices;
 import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -64,10 +64,12 @@
 
     // Internal service impl -- must perform security checks before touching.
     private final WindowManagerService mInternal;
+    private final LetterboxConfiguration mLetterboxConfiguration;
 
     public WindowManagerShellCommand(WindowManagerService service) {
         mInterface = service;
         mInternal = service;
+        mLetterboxConfiguration = service.mLetterboxConfiguration;
     }
 
     @Override
@@ -119,34 +121,12 @@
                     return runGetIgnoreOrientationRequest(pw);
                 case "dump-visible-window-views":
                     return runDumpVisibleWindowViews(pw);
-                case "set-fixed-orientation-letterbox-aspect-ratio":
-                    return runSetFixedOrientationLetterboxAspectRatio(pw);
-                case "get-fixed-orientation-letterbox-aspect-ratio":
-                    return runGetFixedOrientationLetterboxAspectRatio(pw);
-                case "set-letterbox-activity-corners-radius":
-                    return runSetLetterboxActivityCornersRadius(pw);
-                case "get-letterbox-activity-corners-radius":
-                    return runGetLetterboxActivityCornersRadius(pw);
-                case "set-letterbox-background-type":
-                    return runSetLetterboxBackgroundType(pw);
-                case "get-letterbox-background-type":
-                    return runGetLetterboxBackgroundType(pw);
-                case "set-letterbox-background-color":
-                    return runSetLetterboxBackgroundColor(pw);
-                case "get-letterbox-background-color":
-                    return runGetLetterboxBackgroundColor(pw);
-                case "set-letterbox-background-wallpaper-blur-radius":
-                    return runSetLetterboxBackgroundWallpaperBlurRadius(pw);
-                case "get-letterbox-background-wallpaper-blur-radius":
-                    return runGetLetterboxBackgroundWallpaperBlurRadius(pw);
-                case "set-letterbox-background-wallpaper-dark-scrim-alpha":
-                    return runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
-                case "get-letterbox-background-wallpaper-dark-scrim-alpha":
-                    return runGetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
-                case "set-letterbox-horizontal-position-multiplier":
-                    return runSeLetterboxHorizontalPositionMultiplier(pw);
-                case "get-letterbox-horizontal-position-multiplier":
-                    return runGetLetterboxHorizontalPositionMultiplier(pw);
+                case "set-letterbox-style":
+                    return runSetLetterboxStyle(pw);
+                case "get-letterbox-style":
+                    return runGetLetterboxStyle(pw);
+                case "reset-letterbox-style":
+                    return runResetLetterboxStyle(pw);
                 case "set-sandbox-display-apis":
                     return runSandboxDisplayApis(pw);
                 case "reset":
@@ -611,12 +591,6 @@
         final float aspectRatio;
         try {
             String arg = getNextArgRequired();
-            if ("reset".equals(arg)) {
-                synchronized (mInternal.mGlobalLock) {
-                    mInternal.resetFixedOrientationLetterboxAspectRatio();
-                }
-                return 0;
-            }
             aspectRatio = Float.parseFloat(arg);
         } catch (NumberFormatException  e) {
             getErrPrintWriter().println("Error: bad aspect ratio format " + e);
@@ -627,19 +601,7 @@
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
-            mInternal.setFixedOrientationLetterboxAspectRatio(aspectRatio);
-        }
-        return 0;
-    }
-
-    private int runGetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            final float aspectRatio = mInternal.getFixedOrientationLetterboxAspectRatio();
-            if (aspectRatio <= WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
-                pw.println("Letterbox aspect ratio is not set");
-            } else {
-                pw.println("Letterbox aspect ratio is " + aspectRatio);
-            }
+            mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
         }
         return 0;
     }
@@ -648,12 +610,6 @@
         final int cornersRadius;
         try {
             String arg = getNextArgRequired();
-            if ("reset".equals(arg)) {
-                synchronized (mInternal.mGlobalLock) {
-                    mInternal.resetLetterboxActivityCornersRadius();
-                }
-                return 0;
-            }
             cornersRadius = Integer.parseInt(arg);
         } catch (NumberFormatException  e) {
             getErrPrintWriter().println("Error: bad corners radius format " + e);
@@ -664,110 +620,59 @@
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
-            mInternal.setLetterboxActivityCornersRadius(cornersRadius);
-        }
-        return 0;
-    }
-
-    private int runGetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            final int cornersRadius = mInternal.getLetterboxActivityCornersRadius();
-            if (cornersRadius < 0) {
-                pw.println("Letterbox corners radius is not set");
-            } else {
-                pw.println("Letterbox corners radius is " + cornersRadius);
-            }
+            mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
         }
         return 0;
     }
 
     private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
         @LetterboxBackgroundType final int backgroundType;
-
-        String arg = getNextArgRequired();
-        if ("reset".equals(arg)) {
-            synchronized (mInternal.mGlobalLock) {
-                mInternal.resetLetterboxBackgroundType();
-            }
-            return 0;
-        }
-        switch (arg) {
-            case "solid_color":
-                backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
-                break;
-            case "app_color_background":
-                backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-                break;
-            case "app_color_background_floating":
-                backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-                break;
-            case "wallpaper":
-                backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
-                break;
-            default:
-                getErrPrintWriter().println(
-                        "Error: 'reset', 'solid_color', 'app_color_background' or "
-                        + "'wallpaper' should be provided as an argument");
-                return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mInternal.setLetterboxBackgroundType(backgroundType);
-        }
-        return 0;
-    }
-
-    private int runGetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            @LetterboxBackgroundType final int backgroundType =
-                    mInternal.getLetterboxBackgroundType();
-            switch (backgroundType) {
-                case LETTERBOX_BACKGROUND_SOLID_COLOR:
-                    pw.println("Letterbox background type is 'solid_color'");
+        try {
+            String arg = getNextArgRequired();
+            switch (arg) {
+                case "solid_color":
+                    backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
                     break;
-                case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
-                    pw.println("Letterbox background type is 'app_color_background'");
+                case "app_color_background":
+                    backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
                     break;
-                case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
-                    pw.println("Letterbox background type is 'app_color_background_floating'");
+                case "app_color_background_floating":
+                    backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
                     break;
-                case LETTERBOX_BACKGROUND_WALLPAPER:
-                    pw.println("Letterbox background type is 'wallpaper'");
+                case "wallpaper":
+                    backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
                     break;
                 default:
-                    throw new AssertionError(
-                            "Unexpected letterbox background type: " + backgroundType);
+                    getErrPrintWriter().println(
+                            "Error: 'reset', 'solid_color', 'app_color_background' or "
+                            + "'wallpaper' should be provided as an argument");
+                    return -1;
             }
+        } catch (IllegalArgumentException  e) {
+            getErrPrintWriter().println(
+                    "Error: 'reset', 'solid_color', 'app_color_background' or "
+                        + "'wallpaper' should be provided as an argument" + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
         }
         return 0;
     }
 
     private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
         final Color color;
-        String arg = getNextArgRequired();
         try {
-            if ("reset".equals(arg)) {
-                synchronized (mInternal.mGlobalLock) {
-                    mInternal.resetLetterboxBackgroundColor();
-                }
-                return 0;
-            }
+            String arg = getNextArgRequired();
             color = Color.valueOf(Color.parseColor(arg));
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
                     "Error: 'reset' or color in #RRGGBB format should be provided as "
-                            + "an argument " + e + " but got " + arg);
+                            + "an argument " + e);
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
-            mInternal.setLetterboxBackgroundColor(color);
-        }
-        return 0;
-    }
-
-    private int runGetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            final Color color = mInternal.getLetterboxBackgroundColor();
-            pw.println("Letterbox background color is " + Integer.toHexString(color.toArgb()));
+            mLetterboxConfiguration.setLetterboxBackgroundColor(color);
         }
         return 0;
     }
@@ -777,12 +682,6 @@
         final int radius;
         try {
             String arg = getNextArgRequired();
-            if ("reset".equals(arg)) {
-                synchronized (mInternal.mGlobalLock) {
-                    mInternal.resetLetterboxBackgroundWallpaperBlurRadius();
-                }
-                return 0;
-            }
             radius = Integer.parseInt(arg);
         } catch (NumberFormatException  e) {
             getErrPrintWriter().println("Error: blur radius format " + e);
@@ -793,20 +692,7 @@
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
-            mInternal.setLetterboxBackgroundWallpaperBlurRadius(radius);
-        }
-        return 0;
-    }
-
-    private int runGetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
-            throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            final int radius = mInternal.getLetterboxBackgroundWallpaperBlurRadius();
-            if (radius <= 0) {
-                pw.println("Letterbox background wallpaper blur radius is not set");
-            } else {
-                pw.println("Letterbox background wallpaper blur radius is " + radius);
-            }
+            mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
         }
         return 0;
     }
@@ -816,12 +702,6 @@
         final float alpha;
         try {
             String arg = getNextArgRequired();
-            if ("reset".equals(arg)) {
-                synchronized (mInternal.mGlobalLock) {
-                    mInternal.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
-                }
-                return 0;
-            }
             alpha = Float.parseFloat(arg);
         } catch (NumberFormatException  e) {
             getErrPrintWriter().println("Error: bad alpha format " + e);
@@ -832,20 +712,7 @@
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
-            mInternal.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
-        }
-        return 0;
-    }
-
-    private int runGetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
-            throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            final float alpha = mInternal.getLetterboxBackgroundWallpaperDarkScrimAlpha();
-            if (alpha < 0 || alpha >= 1) {
-                pw.println("Letterbox dark scrim alpha is not set");
-            } else {
-                pw.println("Letterbox dark scrim alpha is " + alpha);
-            }
+            mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
         }
         return 0;
     }
@@ -854,12 +721,6 @@
         final float multiplier;
         try {
             String arg = getNextArgRequired();
-            if ("reset".equals(arg)) {
-                synchronized (mInternal.mGlobalLock) {
-                    mInternal.resetLetterboxHorizontalPositionMultiplier();
-                }
-                return 0;
-            }
             multiplier = Float.parseFloat(arg);
         } catch (NumberFormatException  e) {
             getErrPrintWriter().println("Error: bad multiplier format " + e);
@@ -870,23 +731,121 @@
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
-            mInternal.setLetterboxHorizontalPositionMultiplier(multiplier);
+            mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
         }
         return 0;
     }
 
-    private int runGetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            final float multiplier = mInternal.getLetterboxHorizontalPositionMultiplier();
-            if (multiplier < 0) {
-                pw.println("Letterbox horizontal position multiplier is not set");
-            } else {
-                pw.println("Letterbox horizontal position multiplier is " + multiplier);
+    private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
+        if (peekNextArg() == null) {
+            getErrPrintWriter().println("Error: No arguments provided.");
+        }
+        while (peekNextArg() != null) {
+            String arg = getNextArg();
+            switch (arg) {
+                case "--aspectRatio":
+                    runSetFixedOrientationLetterboxAspectRatio(pw);
+                    break;
+                case "--cornerRadius":
+                    runSetLetterboxActivityCornersRadius(pw);
+                    break;
+                case "--backgroundType":
+                    runSetLetterboxBackgroundType(pw);
+                    break;
+                case "--backgroundColor":
+                    runSetLetterboxBackgroundColor(pw);
+                    break;
+                case "--wallpaperBlurRadius":
+                    runSetLetterboxBackgroundWallpaperBlurRadius(pw);
+                    break;
+                case "--wallpaperDarkScrimAlpha":
+                    runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+                    break;
+                case "--horizontalPositionMultiplier":
+                    runSeLetterboxHorizontalPositionMultiplier(pw);
+                    break;
+                default:
+                    getErrPrintWriter().println(
+                            "Error: Unrecognized letterbox style option: " + arg);
+                    return -1;
             }
         }
         return 0;
     }
 
+    private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
+        if (peekNextArg() == null) {
+            resetLetterboxStyle();
+        }
+        synchronized (mInternal.mGlobalLock) {
+            while (peekNextArg() != null) {
+                String arg = getNextArg();
+                switch (arg) {
+                    case "aspectRatio":
+                        mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+                        break;
+                    case "cornerRadius":
+                        mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+                        break;
+                    case "backgroundType":
+                        mLetterboxConfiguration.resetLetterboxBackgroundType();
+                        break;
+                    case "backgroundColor":
+                        mLetterboxConfiguration.resetLetterboxBackgroundColor();
+                        break;
+                    case "wallpaperBlurRadius":
+                        mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+                        break;
+                    case "wallpaperDarkScrimAlpha":
+                        mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+                        break;
+                    case "horizontalPositionMultiplier":
+                        mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+                        break;
+                    default:
+                        getErrPrintWriter().println(
+                                "Error: Unrecognized letterbox style option: " + arg);
+                        return -1;
+                }
+            }
+        }
+        return 0;
+    }
+
+    private void resetLetterboxStyle() {
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+            mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+            mLetterboxConfiguration.resetLetterboxBackgroundType();
+            mLetterboxConfiguration.resetLetterboxBackgroundColor();
+            mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+            mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+            mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+        }
+    }
+
+    private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
+        synchronized (mInternal.mGlobalLock) {
+            pw.println("Corner radius: "
+                    + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
+            pw.println("Horizontal position multiplier: "
+                    + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+            pw.println("Aspect ratio: "
+                    + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+
+            pw.println("Background type: "
+                    + LetterboxConfiguration.letterboxBackgroundTypeToString(
+                            mLetterboxConfiguration.getLetterboxBackgroundType()));
+            pw.println("    Background color: " + Integer.toHexString(
+                    mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
+            pw.println("    Wallpaper blur radius: "
+                    + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
+            pw.println("    Wallpaper dark scrim alpha: "
+                    + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+        }
+        return 0;
+    }
+
     private int runReset(PrintWriter pw) throws RemoteException {
         int displayId = getDisplayId(getNextArg());
 
@@ -911,26 +870,8 @@
         // set-ignore-orientation-request
         mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
 
-        // set-fixed-orientation-letterbox-aspect-ratio
-        mInternal.resetFixedOrientationLetterboxAspectRatio();
-
-        // set-letterbox-activity-corners-radius
-        mInternal.resetLetterboxActivityCornersRadius();
-
-        // set-letterbox-background-type
-        mInternal.resetLetterboxBackgroundType();
-
-        // set-letterbox-background-color
-        mInternal.resetLetterboxBackgroundColor();
-
-        // set-letterbox-background-wallpaper-blur-radius
-        mInternal.resetLetterboxBackgroundWallpaperBlurRadius();
-
-        // set-letterbox-background-wallpaper-dark-scrim-alpha
-        mInternal.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
-
-        // set-letterbox-horizontal-position-multiplier
-        mInternal.resetLetterboxHorizontalPositionMultiplier();
+        // set-letterbox-style
+        resetLetterboxStyle();
 
         // set-sandbox-display-apis
         mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
@@ -966,47 +907,13 @@
         pw.println("  set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
         pw.println("  get-ignore-orientation-request [-d DISPLAY_ID] ");
         pw.println("    If app requested orientation should be ignored.");
-        pw.println("  set-fixed-orientation-letterbox-aspect-ratio [reset|aspectRatio]");
-        pw.println("  get-fixed-orientation-letterbox-aspect-ratio");
-        pw.println("    Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
-                + WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
-        pw.println("    both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will be");
-        pw.println("    ignored and framework implementation will determine aspect ratio.");
-        pw.println("  set-letterbox-activity-corners-radius [reset|cornersRadius]");
-        pw.println("  get-letterbox-activity-corners-radius");
-        pw.println("    Corners radius for activities in the letterbox mode. If radius < 0,");
-        pw.println("    both it and R.integer.config_letterboxActivityCornersRadius will be");
-        pw.println("    ignored and corners of the activity won't be rounded.");
-        pw.println("  set-letterbox-background-color [reset|colorName|'\\#RRGGBB']");
-        pw.println("  get-letterbox-background-color");
-        pw.println("    Color of letterbox background which is be used when letterbox background");
-        pw.println("    type is 'solid-color'. Use get(set)-letterbox-background-type to check");
-        pw.println("    and control letterbox background type. See Color#parseColor for allowed");
-        pw.println("    color formats (#RRGGBB and some colors by name, e.g. magenta or olive). ");
-        pw.println("  set-letterbox-background-type [reset|solid_color|app_color_background");
-        pw.println("    |app_color_background_floating|wallpaper]");
-        pw.println("  get-letterbox-background-type");
-        pw.println("    Type of background used in the letterbox mode.");
-        pw.println("  set-letterbox-background-wallpaper-blur-radius [reset|radius]");
-        pw.println("  get-letterbox-background-wallpaper-blur-radius");
-        pw.println("    Blur radius for 'wallpaper' letterbox background. If radius <= 0");
-        pw.println("    both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius are ");
-        pw.println("    ignored and 0 is used.");
-        pw.println("  set-letterbox-background-wallpaper-dark-scrim-alpha [reset|alpha]");
-        pw.println("  get-letterbox-background-wallpaper-dark-scrim-alpha");
-        pw.println("    Alpha of a black translucent scrim shown over 'wallpaper'");
-        pw.println("    letterbox background. If alpha < 0 or >= 1 both it and");
-        pw.println("    R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored and ");
-        pw.println("    0.0 (transparent) is used instead.");
-        pw.println("  set-letterbox-horizontal-position-multiplier [reset|multiplier]");
-        pw.println("  get-letterbox-horizontal-position-multiplier");
-        pw.println("    horizontal position of a center of a letterboxed app. If it < 0 or > 1");
-        pw.println("    then both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
-        pw.println("    are ignored and central position (0.5) is used.");
         pw.println("  set-sandbox-display-apis [true|1|false|0]");
         pw.println("    Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
         pw.println("    DisplayArea of the activity, or the window bounds if in letterbox or");
         pw.println("    Size Compat Mode.");
+
+        printLetterboxHelp(pw);
+
         pw.println("  reset [-d DISPLAY_ID]");
         pw.println("    Reset all override settings.");
         if (!IS_USER) {
@@ -1016,4 +923,47 @@
             pw.println("    Logging settings.");
         }
     }
+
+    private void printLetterboxHelp(PrintWriter pw) {
+        pw.println("  set-letterbox-style");
+        pw.println("    Sets letterbox style using the following options:");
+        pw.println("      --aspectRatio aspectRatio");
+        pw.println("        Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+                + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+        pw.println("        both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+        pw.println("        be ignored and framework implementation will determine aspect ratio.");
+        pw.println("      --cornerRadius radius");
+        pw.println("        Corners radius for activities in the letterbox mode. If radius < 0,");
+        pw.println("        both it and R.integer.config_letterboxActivityCornersRadius will be");
+        pw.println("        ignored and corners of the activity won't be rounded.");
+        pw.println("      --backgroundType [reset|solid_color|app_color_background");
+        pw.println("          |app_color_background_floating|wallpaper]");
+        pw.println("        Type of background used in the letterbox mode.");
+        pw.println("      --backgroundColor color");
+        pw.println("        Color of letterbox which is be used when letterbox background type");
+        pw.println("        is 'solid-color'. Use (set)get-letterbox-style to check and control");
+        pw.println("        letterbox background type. See Color#parseColor for allowed color");
+        pw.println("        formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+        pw.println("      --wallpaperBlurRadius radius");
+        pw.println("        Blur radius for 'wallpaper' letterbox background. If radius <= 0");
+        pw.println("        both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
+        pw.println("        are ignored and 0 is used.");
+        pw.println("      --wallpaperDarkScrimAlpha alpha");
+        pw.println("        Alpha of a black translucent scrim shown over 'wallpaper'");
+        pw.println("        letterbox background. If alpha < 0 or >= 1 both it and");
+        pw.println("        R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
+        pw.println("        and 0.0 (transparent) is used instead.");
+        pw.println("      --horizontalPositionMultiplier multiplier");
+        pw.println("        Horizontal position of app window center. If multiplier < 0 or > 1,");
+        pw.println("        both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
+        pw.println("        are ignored and central position (0.5) is used.");
+        pw.println("  reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
+        pw.println("      |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
+        pw.println("      |horizontalPositionMultiplier]");
+        pw.println("    Resets overrides to default values for specified properties separated");
+        pw.println("    by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
+        pw.println("    If no arguments provided, all values will be reset.");
+        pw.println("  get-letterbox-style");
+        pw.println("    Prints letterbox style configuration.");
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 2e37fee..9382b8e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -36,6 +36,7 @@
 import android.app.WindowConfiguration;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
+import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -45,7 +46,6 @@
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.IDisplayAreaOrganizerController;
 import android.window.ITaskOrganizerController;
@@ -766,18 +766,21 @@
             return false;
         }
 
+        GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+                buffer.getHardwareBuffer());
         SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
                 .setName(wc.getName() + " - Organizer Screenshot")
-                .setBufferSize(bounds.width(), bounds.height())
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .setParent(wc.getParentSurfaceControl())
+                .setSecure(buffer.containsSecureLayers())
                 .setCallsite("WindowOrganizerController.takeScreenshot")
+                .setBLASTLayer()
                 .build();
 
-        Surface surface = new Surface();
-        surface.copyFrom(screenshot);
-        surface.attachAndQueueBufferWithColorSpace(buffer.getHardwareBuffer(), null);
-        surface.release();
+        SurfaceControl.Transaction transaction = mService.mWindowManager.mTransactionFactory.get();
+        transaction.setBuffer(screenshot, graphicBuffer);
+        transaction.setColorSpace(screenshot, buffer.getColorSpace());
+        transaction.apply();
 
         outSurfaceControl.copyFrom(screenshot, "WindowOrganizerController.takeScreenshot");
         return true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 46d923b..1a5042f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1140,7 +1140,7 @@
 
     void attach() {
         if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
-        mSession.windowAddedLocked(mAttrs.packageName);
+        mSession.windowAddedLocked();
     }
 
     /**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cf9b88a..6283b4e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -96,6 +96,7 @@
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
+import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -8735,7 +8736,8 @@
 
         final CallerIdentity caller = getCallerIdentity();
         if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
-                && getManagedUserId(userHandle) == -1) {
+                && getManagedUserId(userHandle) == -1
+                && newState != STATE_USER_UNMANAGED) {
             // No managed device, user or profile, so setting provisioning state makes no sense.
             throw new IllegalStateException("Not allowed to change provisioning state unless a "
                       + "device or profile owner is set.");
@@ -8798,6 +8800,12 @@
             case DevicePolicyManager.STATE_USER_SETUP_FINALIZED:
                 // Cannot transition out of finalized.
                 break;
+            case DevicePolicyManager.STATE_USER_PROFILE_FINALIZED:
+                // Should only move to an unmanaged state after removing the work profile.
+                if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+                    return;
+                }
+                break;
         }
 
         // Didn't meet any of the accepted state transition checks above, throw appropriate error.
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 7654093..d55bbd1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -468,8 +468,9 @@
                 TEST_CALLING_UID);
     }
 
-    private void setPrioritizedAlarm(int type, long triggerTime, IAlarmListener listener) {
-        mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
+    private void setPrioritizedAlarm(int type, long triggerTime, long windowLength,
+            IAlarmListener listener) {
+        mService.setImpl(type, triggerTime, windowLength, 0, null, listener, "test",
                 FLAG_STANDALONE | FLAG_PRIORITIZE, null, null, TEST_CALLING_UID,
                 TEST_CALLING_PACKAGE, null);
     }
@@ -1685,7 +1686,7 @@
         final int numAlarms = 10;
         for (int i = 0; i < numAlarms; i++) {
             setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
-                    new IAlarmListener.Stub() {
+                    0, new IAlarmListener.Stub() {
                         @Override
                         public void doAlarm(IAlarmCompleteListener callback)
                                 throws RemoteException {
@@ -1720,7 +1721,7 @@
         final int numAlarms = 10;
         for (int i = 0; i < numAlarms; i++) {
             setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
-                    new IAlarmListener.Stub() {
+                    0, new IAlarmListener.Stub() {
                         @Override
                         public void doAlarm(IAlarmCompleteListener callback)
                                 throws RemoteException {
@@ -1738,12 +1739,12 @@
         }
         assertEquals(numAlarms, alarmsFired.get());
 
-        setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, new IAlarmListener.Stub() {
+        setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, 0, new IAlarmListener.Stub() {
             @Override
             public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
             }
         });
-        setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, new IAlarmListener.Stub() {
+        setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, 0, new IAlarmListener.Stub() {
             @Override
             public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
             }
@@ -2263,6 +2264,28 @@
     }
 
     @Test
+    public void minWindowPriorityAlarm() {
+        doReturn(true).when(
+                () -> CompatChanges.isChangeEnabled(
+                        eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
+                        anyString(), any(UserHandle.class)));
+        final long minWindow = 73;
+        setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+        // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+        for (int window = 1; window <= minWindow; window++) {
+            setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() {
+                @Override
+                public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+                }
+            });
+            assertEquals(1, mService.mAlarmStore.size());
+            final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+            assertEquals(window, a.windowLength);
+        }
+    }
+
+    @Test
     public void denyListPackagesAdded() {
         mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});
         setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5");
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 89798ce..1b42dfa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3179,6 +3179,16 @@
     }
 
     @Test
+    public void testSetUserProvisioningState_profileFinalized_canTransitionToUserUnmanaged()
+            throws Exception {
+        setupProfileOwner();
+
+        exerciseUserProvisioningTransitions(CALLER_USER_HANDLE,
+                DevicePolicyManager.STATE_USER_PROFILE_FINALIZED,
+                DevicePolicyManager.STATE_USER_UNMANAGED);
+    }
+
+    @Test
     public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
             throws Exception {
         setupProfileOwner();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 4a42940..5d60a89 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -208,4 +208,9 @@
             parcel.recycle();
         }
     }
-}
+
+    @Override
+    void setKeystorePassword(byte[] password, int userHandle) {
+
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
index 2b9a05c..efa1b04 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -20,11 +20,16 @@
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.os.Handler;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 
@@ -94,4 +99,11 @@
     public int checkCallingOrSelfPermission(String permission) {
         return PackageManager.PERMISSION_GRANTED;
     }
+
+    @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver,
+            UserHandle user, IntentFilter filter, String broadcastPermission,
+            Handler scheduler) {
+        return null;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0afd39f..2f52352 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1971,7 +1971,8 @@
 
         // test misc display overrides
         assertEquals(ignoreOrientationRequests, testDisplayContent.mIgnoreOrientationRequest);
-        assertEquals(fixedOrientationLetterboxRatio, mWm.getFixedOrientationLetterboxAspectRatio(),
+        assertEquals(fixedOrientationLetterboxRatio,
+                mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
                 0 /* delta */);
     }
 
@@ -2011,7 +2012,8 @@
 
         // test misc display overrides
         assertEquals(ignoreOrientationRequests, testDisplayContent.mIgnoreOrientationRequest);
-        assertEquals(fixedOrientationLetterboxRatio, mWm.getFixedOrientationLetterboxAspectRatio(),
+        assertEquals(fixedOrientationLetterboxRatio,
+                mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
                 0 /* delta */);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ee1d393..95b7443 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -335,11 +335,11 @@
         final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window");
 
         assertEquals(window, mActivity.findMainWindow());
-        assertTrue(mActivity.isLetterboxed(mActivity.findMainWindow()));
+        assertTrue(mActivity.mLetterboxUiController.isLetterboxed(mActivity.findMainWindow()));
 
         window.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
 
-        assertFalse(mActivity.isLetterboxed(mActivity.findMainWindow()));
+        assertFalse(mActivity.mLetterboxUiController.isLetterboxed(mActivity.findMainWindow()));
     }
 
     @Test
@@ -1023,7 +1023,7 @@
 
         // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
         // orientation letterbox.
-        mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f);
+        mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
         mActivity.info.setMinAspectRatio(3);
         prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
 
@@ -1054,7 +1054,7 @@
 
         // Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed
         // orientation letterbox.
-        mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(3);
+        mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(3);
         prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
 
         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
@@ -1085,7 +1085,7 @@
         // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
         // orientation letterbox.
         final float fixedOrientationLetterboxAspectRatio = 1.1f;
-        mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(
+        mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
                 fixedOrientationLetterboxAspectRatio);
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
@@ -1586,7 +1586,7 @@
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
 
-        mActivity.mWmService.setLetterboxHorizontalPositionMultiplier(
+        mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
                 letterboxHorizontalPositionMultiplier);
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
@@ -1624,7 +1624,7 @@
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
 
-        mActivity.mWmService.setLetterboxHorizontalPositionMultiplier(
+        mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
                 letterboxHorizontalPositionMultiplier);
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index f3616da6c..619aee6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import android.annotation.NonNull;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -253,4 +255,14 @@
     public SurfaceControl.Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) {
         return this;
     }
+
+    @Override
+    public SurfaceControl.Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) {
+        return this;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ae12062..2d4e4ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -208,11 +208,11 @@
         // Ensure letterbox aspect ratio is not overridden on any device target.
         // {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set
         // on some device form factors.
-        mAtm.mWindowManager.setFixedOrientationLetterboxAspectRatio(0);
+        mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(0);
         // Ensure letterbox position multiplier is not overridden on any device target.
         // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
         // may be set on some device form factors.
-        mAtm.mWindowManager.setLetterboxHorizontalPositionMultiplier(0.5f);
+        mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
 
         checkDeviceSpecificOverridesNotApplied();
     }
@@ -220,10 +220,10 @@
     @After
     public void tearDown() throws Exception {
         // Revert back to device overrides.
-        mAtm.mWindowManager.setFixedOrientationLetterboxAspectRatio(
+        mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
                 mContext.getResources().getFloat(
                         com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio));
-        mAtm.mWindowManager.setLetterboxHorizontalPositionMultiplier(
+        mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
                 mContext.getResources().getFloat(
                     com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier));
     }
@@ -235,7 +235,8 @@
     private void checkDeviceSpecificOverridesNotApplied() {
         // Check global overrides
         if (!sGlobalOverridesChecked) {
-            assertEquals(0, mWm.getFixedOrientationLetterboxAspectRatio(), 0 /* delta */);
+            assertEquals(0, mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
+                    0 /* delta */);
             sGlobalOverridesChecked = true;
         }
         // Check display-specific overrides
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 32533cb..0d6cd5a 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -760,6 +760,7 @@
     private static void assignExclusiveSmsPermissionsToSystemApp(Context context,
             PackageManager packageManager, AppOpsManager appOps, String packageName,
             boolean sigatureMatch) {
+        if (packageName == null) return;
         // First check package signature matches the caller's package signature.
         // Since this class is only used internally by the system, this check makes sure
         // the package signature matches system signature.
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index bde62fb..ac01afa 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -134,7 +134,7 @@
      *
      * Range [0, 15] for each CQI.
      */
-    private List<Integer> mCsiCqiReport;;
+    private List<Integer> mCsiCqiReport;
     private int mSsRsrp;
     private int mSsRsrq;
     private int mSsSinr;
@@ -172,13 +172,13 @@
      * @hide
      */
     public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex,
-            List<Integer> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) {
+            List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) {
         mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44);
         mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
         mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
         mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3);
-        mCsiCqiReport =  csiCqiReport.stream()
-                .map(cqi -> new Integer(inRangeOrUnavailable(cqi.intValue(), 1, 3)))
+        mCsiCqiReport = csiCqiReport.stream()
+                .map(cqi -> new Integer(inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 1, 3)))
                 .collect(Collectors.toList());
         mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44);
         mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f0771be..c8e5e56 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4885,8 +4885,9 @@
      * Return the set of IMSIs that should be considered "merged together" for data usage
      * purposes. This API merges IMSIs based on subscription grouping: IMSI of those in the same
      * group will all be returned.
-     * Return the current IMSI if there is no subscription group. See
-     * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group.
+     * Return the current IMSI if there is no subscription group, see
+     * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group,
+     * otherwise return an empty array if there is a failure.
      *
      * @hide
      */
diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
index 6c1c653..5642549 100644
--- a/telephony/java/android/telephony/data/QosBearerFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -31,7 +31,6 @@
 import java.util.List;
 import java.util.Objects;
 
-
 /**
  * Class that stores QOS filter parameters as defined in
  * 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 0593615..a540dff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -23,7 +23,7 @@
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
 
-val LAUNCHER_TITLE = arrayOf("Wallpaper", "Launcher", "com.google.android.googlequicksearchbox")
+val HOME_WINDOW_TITLE = arrayOf("Wallpaper", "Launcher")
 
 fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
     assertWm {
@@ -41,23 +41,23 @@
     assertWm {
         this.showsAppWindowOnTop(testApp.getPackage())
             .then()
-            .showsAppWindowOnTop(*LAUNCHER_TITLE)
+            .showsAppWindowOnTop(*HOME_WINDOW_TITLE)
     }
 }
 
 fun FlickerTestParameter.launcherWindowBecomesVisible() {
     assertWm {
-        this.hidesBelowAppWindow(*LAUNCHER_TITLE)
+        this.hidesBelowAppWindow(*HOME_WINDOW_TITLE)
             .then()
-            .showsBelowAppWindow(*LAUNCHER_TITLE)
+            .showsBelowAppWindow(*HOME_WINDOW_TITLE)
     }
 }
 
 fun FlickerTestParameter.launcherWindowBecomesInvisible() {
     assertWm {
-        this.showsBelowAppWindow(*LAUNCHER_TITLE)
+        this.showsBelowAppWindow(*HOME_WINDOW_TITLE)
             .then()
-            .hidesBelowAppWindow(*LAUNCHER_TITLE)
+            .hidesBelowAppWindow(*HOME_WINDOW_TITLE)
     }
 }
 
@@ -179,7 +179,7 @@
 
 fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) {
     assertLayers {
-        this.isVisible(*LAUNCHER_TITLE)
+        this.isVisible(*HOME_WINDOW_TITLE)
             .then()
             .isVisible(appName)
     }
@@ -190,7 +190,7 @@
         this.isVisible(testApp.getPackage())
             .then()
             .isInvisible(testApp.getPackage())
-            .isVisible(*LAUNCHER_TITLE)
+            .isVisible(*HOME_WINDOW_TITLE)
     }
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 6a7309c..01e34d9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -18,11 +18,11 @@
 
 import android.platform.helpers.IAppHelper
 import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.LAUNCHER_TITLE
+import com.android.server.wm.flicker.HOME_WINDOW_TITLE
 
 fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
     assertWm {
-        this.showsAppWindowOnTop(*LAUNCHER_TITLE)
+        this.showsAppWindowOnTop(*HOME_WINDOW_TITLE)
             .then()
             .showsAppWindowOnTop("Snapshot", testApp.getPackage())
     }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
index f6d9a73..487c856 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -56,6 +56,7 @@
         CanvasProperty<Float> mY;
         CanvasProperty<Float> mRadius;
         CanvasProperty<Float> mProgress;
+        CanvasProperty<Float> mNoisePhase;
         CanvasProperty<Paint> mPaint;
         RuntimeShader mRuntimeShader;
 
@@ -99,6 +100,7 @@
             mY = CanvasProperty.createFloat(200.0f);
             mRadius = CanvasProperty.createFloat(150.0f);
             mProgress = CanvasProperty.createFloat(0.0f);
+            mNoisePhase = CanvasProperty.createFloat(0.0f);
 
             Paint p = new Paint();
             p.setAntiAlias(true);
@@ -115,7 +117,8 @@
 
             if (canvas.isHardwareAccelerated()) {
                 RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
-                recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mRuntimeShader);
+                recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mNoisePhase,
+                        mRuntimeShader);
             }
         }
 
@@ -141,6 +144,9 @@
                     mProgress, mToggle ? 1.0f : 0.0f));
 
             mRunningAnimations.add(new RenderNodeAnimator(
+                    mNoisePhase, DURATION));
+
+            mRunningAnimations.add(new RenderNodeAnimator(
                     mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
 
             // Will be "chained" to run after the above
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6d852bf..9b74583 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -312,7 +312,7 @@
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
         if (isAtLeastS()) {
-            netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            netCap.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
             netCap.setUids(uids);
         }
         if (isAtLeastR()) {
@@ -642,16 +642,16 @@
             assertTrue(nc2.appliesToUid(22));
 
             // Verify the subscription id list can be combined only when they are equal.
-            nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
-            nc2.setSubIds(Set.of(TEST_SUBID2));
+            nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            nc2.setSubscriptionIds(Set.of(TEST_SUBID2));
             assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
 
-            nc2.setSubIds(Set.of());
+            nc2.setSubscriptionIds(Set.of());
             assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
 
-            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
             nc2.combineCapabilities(nc1);
-            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds());
         }
     }
 
@@ -806,20 +806,20 @@
         assertEquals(nc1, nc2);
 
         if (isAtLeastS()) {
-            assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
-            nc1.setSubIds(Set.of());
+            assertThrows(NullPointerException.class, () -> nc1.setSubscriptionIds(null));
+            nc1.setSubscriptionIds(Set.of());
             nc2.set(nc1);
             assertEquals(nc1, nc2);
 
-            nc1.setSubIds(Set.of(TEST_SUBID1));
+            nc1.setSubscriptionIds(Set.of(TEST_SUBID1));
             nc2.set(nc1);
             assertEquals(nc1, nc2);
 
-            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
             nc2.set(nc1);
             assertEquals(nc1, nc2);
 
-            nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+            nc2.setSubscriptionIds(Set.of(TEST_SUBID3, TEST_SUBID2));
             assertNotEquals(nc1, nc2);
         }
     }
@@ -908,8 +908,8 @@
         // satisfy these requests.
         final NetworkCapabilities nc = new NetworkCapabilities.Builder()
                 .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
-                .setSubIds(new ArraySet<>(subIds)).build();
-        assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+                .setSubscriptionIds(new ArraySet<>(subIds)).build();
+        assertEquals(new ArraySet<>(subIds), nc.getSubscriptionIds());
         return nc;
     }
 
@@ -921,11 +921,11 @@
         final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
 
         final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
-        assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+        assertEmpty(requestWithoutId.networkCapabilities.getSubscriptionIds());
         final NetworkRequest requestWithIds = new NetworkRequest.Builder()
-                .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+                .setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
         assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
-                requestWithIds.networkCapabilities.getSubIds());
+                requestWithIds.networkCapabilities.getSubscriptionIds());
 
         assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
         assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
@@ -1138,8 +1138,8 @@
 
         if (isAtLeastS()) {
             final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
-                    .setSubIds(Set.of(TEST_SUBID1)).build();
-            assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+                    .setSubscriptionIds(Set.of(TEST_SUBID1)).build();
+            assertEquals(Set.of(TEST_SUBID1), nc2.getSubscriptionIds());
         }
     }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 039ce2f..b71be59 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2668,25 +2668,6 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        // Bring up wifi with a score of 70.
-        // Cell is lingered because it would not satisfy any request, even if it validated.
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.adjustScore(50);
-        mWiFiNetworkAgent.connect(false);   // Score: 70
-        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
-        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
-        assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-
-        // Tear down wifi.
-        mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
-        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
-        assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-
         // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
         // it's arguably correct to linger it, since it was the default network before it validated.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2991,11 +2972,17 @@
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
-        // BUG: the network will no longer linger, even though it's validated and outscored.
-        // TODO: fix this.
         mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
+        // BUG: with the legacy int-based scoring code, the network will no longer linger, even
+        // though it's validated and outscored.
+        // The new policy-based scoring code fixes this.
+        // TODO: remove the line below and replace with the three commented lines when
+        // the policy-based scoring code is turned on.
         callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+        // callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
+        // callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
+        // callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.assertNoCallback();
 
@@ -12602,12 +12589,12 @@
     public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception {
         mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
         final NetworkCapabilities nc = new NetworkCapabilities();
-        nc.setSubIds(Collections.singleton(Process.myUid()));
+        nc.setSubscriptionIds(Collections.singleton(Process.myUid()));
 
         final NetworkCapabilities result =
                 mService.networkCapabilitiesRestrictedForCallerPermissions(
                         nc, Process.myPid(), Process.myUid());
-        assertTrue(result.getSubIds().isEmpty());
+        assertTrue(result.getSubscriptionIds().isEmpty());
     }
 
     @Test
@@ -12616,17 +12603,17 @@
 
         final Set<Integer> subIds = Collections.singleton(Process.myUid());
         final NetworkCapabilities nc = new NetworkCapabilities();
-        nc.setSubIds(subIds);
+        nc.setSubscriptionIds(subIds);
 
         final NetworkCapabilities result =
                 mService.networkCapabilitiesRestrictedForCallerPermissions(
                         nc, Process.myPid(), Process.myUid());
-        assertEquals(subIds, result.getSubIds());
+        assertEquals(subIds, result.getSubscriptionIds());
     }
 
     private NetworkRequest getRequestWithSubIds() {
         return new NetworkRequest.Builder()
-                .setSubIds(Collections.singleton(Process.myUid()))
+                .setSubscriptionIds(Collections.singleton(Process.myUid()))
                 .build();
     }
 
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9a66343..907cb46 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -696,7 +696,7 @@
                         .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                         .addTransportType(transport);
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            ncBuilder.setSubIds(Collections.singleton(subId));
+            ncBuilder.setSubscriptionIds(Collections.singleton(subId));
         }
 
         return ncBuilder;
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 0c7363e..8289e85 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -189,7 +189,7 @@
     private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
         return getExpectedRequestBase()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                .setSubIds(netCapsSubIds)
+                .setSubscriptionIds(netCapsSubIds)
                 .build();
     }
 
@@ -201,7 +201,7 @@
     }
 
     private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
-        return getExpectedRequestBase().setSubIds(netCapsSubIds).build();
+        return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
     }
 
     private NetworkRequest.Builder getExpectedRequestBase() {
diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
deleted file mode 100755
index 72fb867..0000000
--- a/tools/hiddenapi/checksorted_sha.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-set -e
-LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "hiddenapi/hiddenapi-.*txt" | while read file; do
-    diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh )  || {
-      echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
-      echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $PWD/$file\e[0m"
-      exit 1
-    }
-done
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
deleted file mode 100755
index 710da40..0000000
--- a/tools/hiddenapi/sort_api.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-set -e
-if [ -z "$1" ]; then
-  source_list=/dev/stdin
-  dest_list=/dev/stdout
-else
-  source_list="$1"
-  dest_list="$1"
-fi
-# Load the file
-readarray A < "$source_list"
-# Sort
-IFS=$'\n'
-# Stash away comments
-C=( $(grep -E '^#' <<< "${A[*]}" || :) )
-A=( $(grep -v -E '^#' <<< "${A[*]}" || :) )
-# Sort entries
-A=( $(LC_COLLATE=C sort -f <<< "${A[*]}") )
-A=( $(uniq <<< "${A[*]}") )
-# Concatenate comments and entries
-A=( ${C[*]} ${A[*]} )
-unset IFS
-# Dump array back into the file
-if [ ${#A[@]} -ne 0 ]; then
-  printf '%s\n' "${A[@]}" > "$dest_list"
-fi
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index da0571ba..3b75660 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -117,13 +117,13 @@
     /**
      * Interface used to listen country code event
      */
-    public interface CountryCodeChangeListener {
+    public interface CountryCodeChangedListener {
         /**
          * Called when country code changed.
          *
-         * @param countryCode A new country code which is 2-Character alphanumeric.
+         * @param countryCode An ISO-3166-alpha2 country code which is 2-Character alphanumeric.
          */
-        void onChanged(@NonNull String countryCode);
+        void onCountryCodeChanged(@NonNull String countryCode);
     }
 
     /**
@@ -163,27 +163,27 @@
     /** @hide */
     @VisibleForTesting
     public class WificondEventHandler extends IWificondEventCallback.Stub {
-        private Map<CountryCodeChangeListener, Executor> mCountryCodeChangeListenerHolder =
+        private Map<CountryCodeChangedListener, Executor> mCountryCodeChangedListenerHolder =
                 new HashMap<>();
 
         /**
-         * Register CountryCodeChangeListener with pid.
+         * Register CountryCodeChangedListener with pid.
          *
          * @param executor The Executor on which to execute the callbacks.
          * @param listener listener for country code changed events.
          */
-        public void registerCountryCodeChangeListener(Executor executor,
-                CountryCodeChangeListener listener) {
-            mCountryCodeChangeListenerHolder.put(listener, executor);
+        public void registerCountryCodeChangedListener(Executor executor,
+                CountryCodeChangedListener listener) {
+            mCountryCodeChangedListenerHolder.put(listener, executor);
         }
 
         /**
-         * Unregister CountryCodeChangeListener with pid.
+         * Unregister CountryCodeChangedListener with pid.
          *
          * @param listener listener which registered country code changed events.
          */
-        public void unregisterCountryCodeChangeListener(CountryCodeChangeListener listener) {
-            mCountryCodeChangeListenerHolder.remove(listener);
+        public void unregisterCountryCodeChangedListener(CountryCodeChangedListener listener) {
+            mCountryCodeChangedListenerHolder.remove(listener);
         }
 
         @Override
@@ -191,8 +191,8 @@
             Log.d(TAG, "OnRegDomainChanged " + countryCode);
             final long token = Binder.clearCallingIdentity();
             try {
-                mCountryCodeChangeListenerHolder.forEach((listener, executor) -> {
-                    executor.execute(() -> listener.onChanged(countryCode));
+                mCountryCodeChangedListenerHolder.forEach((listener, executor) -> {
+                    executor.execute(() -> listener.onCountryCodeChanged(countryCode));
                 });
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -1240,25 +1240,25 @@
      * @param listener listener for country code changed events.
      * @return true on success, false on failure.
      */
-    public boolean registerCountryCodeChangeListener(@NonNull @CallbackExecutor Executor executor,
-            @NonNull CountryCodeChangeListener listener) {
+    public boolean registerCountryCodeChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull CountryCodeChangedListener listener) {
         if (!retrieveWificondAndRegisterForDeath()) {
             return false;
         }
         Log.d(TAG, "registerCountryCodeEventListener called");
-        mWificondEventHandler.registerCountryCodeChangeListener(executor, listener);
+        mWificondEventHandler.registerCountryCodeChangedListener(executor, listener);
         return true;
     }
 
 
     /**
-     * Unregister CountryCodeChangeListener with pid.
+     * Unregister CountryCodeChangedListener with pid.
      *
      * @param listener listener which registered country code changed events.
      */
-    public void unregisterCountryCodeChangeListener(@NonNull CountryCodeChangeListener listener) {
+    public void unregisterCountryCodeChangedListener(@NonNull CountryCodeChangedListener listener) {
         Log.d(TAG, "unregisterCountryCodeEventListener called");
-        mWificondEventHandler.unregisterCountryCodeChangeListener(listener);
+        mWificondEventHandler.unregisterCountryCodeChangedListener(listener);
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 98a0042..3fb2301 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -98,9 +98,9 @@
     @Mock
     private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback;
     @Mock
-    private WifiNl80211Manager.CountryCodeChangeListener mCountryCodeChangeListener;
+    private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener;
     @Mock
-    private WifiNl80211Manager.CountryCodeChangeListener mCountryCodeChangeListener2;
+    private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener2;
     @Mock
     private Context mContext;
     private TestLooper mLooper;
@@ -768,25 +768,25 @@
     }
 
     /**
-     * Ensures callback works after register CountryCodeChangeListener.
+     * Ensures callback works after register CountryCodeChangedListener.
      */
     @Test
-    public void testCountryCodeChangeListenerInvocation() throws Exception {
-        assertTrue(mWificondControl.registerCountryCodeChangeListener(
-                Runnable::run, mCountryCodeChangeListener));
-        assertTrue(mWificondControl.registerCountryCodeChangeListener(
-                Runnable::run, mCountryCodeChangeListener2));
+    public void testCountryCodeChangedListenerInvocation() throws Exception {
+        assertTrue(mWificondControl.registerCountryCodeChangedListener(
+                Runnable::run, mCountryCodeChangedListener));
+        assertTrue(mWificondControl.registerCountryCodeChangedListener(
+                Runnable::run, mCountryCodeChangedListener2));
 
         mWificondEventHandler.OnRegDomainChanged(TEST_COUNTRY_CODE);
-        verify(mCountryCodeChangeListener).onChanged(TEST_COUNTRY_CODE);
-        verify(mCountryCodeChangeListener2).onChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangedListener).onCountryCodeChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangedListener2).onCountryCodeChanged(TEST_COUNTRY_CODE);
 
-        reset(mCountryCodeChangeListener);
-        reset(mCountryCodeChangeListener2);
-        mWificondControl.unregisterCountryCodeChangeListener(mCountryCodeChangeListener2);
+        reset(mCountryCodeChangedListener);
+        reset(mCountryCodeChangedListener2);
+        mWificondControl.unregisterCountryCodeChangedListener(mCountryCodeChangedListener2);
         mWificondEventHandler.OnRegDomainChanged(TEST_COUNTRY_CODE);
-        verify(mCountryCodeChangeListener).onChanged(TEST_COUNTRY_CODE);
-        verify(mCountryCodeChangeListener2, never()).onChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangedListener).onCountryCodeChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangedListener2, never()).onCountryCodeChanged(TEST_COUNTRY_CODE);
     }
 
     /**